From bb4f798a04ba9818543baf1269198a00c1a8a86d Mon Sep 17 00:00:00 2001 From: Michael Biebl Date: Fri, 19 Apr 2019 01:12:47 +0200 Subject: [PATCH] New upstream version 242 --- .ctags | 1 + .dir-locals.el | 3 +- .../PotentiallyDangerousFunction.ql | 54 + .lgtm/cpp-queries/fgets.ql | 21 - .mailmap | 2 + .mkosi/mkosi.arch | 2 +- NEWS | 286 +- README | 4 +- README.md | 1 + TODO | 62 +- coccinelle/empty-or-dash.cocci | 5 + coccinelle/log-json.cocci | 8 + docs/BOOT_LOADER_SPECIFICATION.md | 49 +- docs/DISTRO_PORTING.md | 18 +- docs/ENVIRONMENT.md | 45 + docs/PORTABLE_SERVICES.md | 45 +- docs/RELEASE.md | 19 +- docs/TEMPORARY_DIRECTORIES.md | 220 + docs/TRANSIENT-SETTINGS.md | 6 +- hwdb/20-OUI.hwdb | 1167 ++++- hwdb/20-acpi-vendor.hwdb | 4 +- hwdb/20-acpi-vendor.hwdb.patch | 8 +- hwdb/20-pci-vendor-model.hwdb | 488 +- hwdb/20-usb-vendor-model.hwdb | 203 +- hwdb/60-evdev.hwdb | 26 + hwdb/60-input-id.hwdb | 4 + hwdb/60-keyboard.hwdb | 12 +- hwdb/60-sensor.hwdb | 22 + hwdb/70-mouse.hwdb | 2 +- hwdb/70-touchpad.hwdb | 8 + hwdb/acpi-update.py | 4 +- hwdb/ids_parser.py | 49 +- hwdb/ma-large.txt | 4421 +++++++++++------ hwdb/ma-medium.txt | 1262 +++-- hwdb/ma-small.txt | 1397 ++++-- hwdb/pci.ids | 202 +- hwdb/usb.ids | 73 +- man/binfmt.d.xml | 7 +- man/bootctl.xml | 56 +- man/bootup.xml | 44 +- man/busctl.xml | 29 +- man/coredump.conf.xml | 9 +- man/coredumpctl.xml | 7 +- man/crypttab.xml | 23 +- man/daemon.xml | 18 +- man/dnssec-trust-anchors.d.xml | 7 +- man/environment.d.xml | 3 +- man/file-hierarchy.xml | 73 +- man/halt.xml | 7 +- man/hostname.xml | 7 +- man/hostnamectl.xml | 7 +- man/hwdb.xml | 7 +- man/journal-remote.conf.xml | 2 +- man/journal-upload.conf.xml | 7 +- man/journalctl.xml | 28 +- man/journald.conf.xml | 7 +- man/kernel-command-line.xml | 18 +- man/kernel-install.xml | 84 +- man/less-variables.xml | 9 +- man/libsystemd-pkgconfig.xml | 9 +- man/libudev.xml | 7 +- man/loader.conf.xml | 3 +- man/locale.conf.xml | 7 +- man/localectl.xml | 7 +- man/localtime.xml | 7 +- man/loginctl.xml | 7 +- man/logind.conf.xml | 7 +- man/machine-id.xml | 7 +- man/machine-info.xml | 7 +- man/machinectl.xml | 7 +- man/meson.build | 5 +- man/modules-load.d.xml | 7 +- man/networkctl.xml | 48 +- man/networkd.conf.xml | 2 +- man/nss-myhostname.xml | 7 +- man/nss-mymachines.xml | 7 +- man/nss-resolve.xml | 7 +- man/nss-systemd.xml | 7 +- man/os-release.xml | 7 +- man/pam_systemd.xml | 7 +- man/portablectl.xml | 3 +- man/resolvectl.xml | 9 +- man/resolved.conf.xml | 7 +- man/rules/meson.build | 4 +- man/runlevel.xml | 7 +- man/sd-bus-errors.xml | 9 +- man/sd-bus.xml | 7 +- man/sd-daemon.xml | 7 +- man/sd-event.xml | 7 +- man/sd-id128.xml | 7 +- man/sd-journal.xml | 7 +- man/sd-login.xml | 7 +- man/sd_booted.xml | 7 +- man/sd_bus_add_match.xml | 4 +- man/sd_bus_attach_event.xml | 29 +- man/sd_bus_close.xml | 29 +- man/sd_bus_creds_get_pid.xml | 92 +- man/sd_bus_creds_new_from_pid.xml | 57 +- man/sd_bus_default.xml | 50 +- man/sd_bus_error.xml | 40 +- man/sd_bus_error_add_map.xml | 37 +- man/sd_bus_get_fd.xml | 54 +- man/sd_bus_get_n_queued_read.xml | 29 +- man/sd_bus_is_open.xml | 29 +- man/sd_bus_message_append.xml | 16 +- man/sd_bus_message_append_array.xml | 17 +- man/sd_bus_message_append_basic.xml | 69 +- man/sd_bus_message_append_string_memfd.xml | 17 +- man/sd_bus_message_append_strv.xml | 17 +- man/sd_bus_message_copy.xml | 70 +- man/sd_bus_message_get_cookie.xml | 51 +- man/sd_bus_message_get_monotonic_usec.xml | 44 +- man/sd_bus_message_get_signature.xml | 34 +- man/sd_bus_message_get_type.xml | 24 +- man/sd_bus_message_new.xml | 40 +- man/sd_bus_message_new_method_call.xml | 84 +- man/sd_bus_message_new_method_error.xml | 73 +- man/sd_bus_message_new_signal.xml | 54 +- man/sd_bus_message_read.xml | 21 +- man/sd_bus_message_read_array.xml | 50 +- man/sd_bus_message_read_basic.xml | 44 +- man/sd_bus_message_rewind.xml | 42 +- man/sd_bus_message_set_destination.xml | 49 +- man/sd_bus_message_set_expect_reply.xml | 40 +- man/sd_bus_message_skip.xml | 58 +- man/sd_bus_message_verify_type.xml | 38 +- man/sd_bus_negotiate_fds.xml | 29 +- man/sd_bus_new.xml | 29 +- man/sd_bus_path_encode.xml | 9 +- man/sd_bus_process.xml | 66 +- man/sd_bus_reply_method_error.xml | 81 +- man/sd_bus_request_name.xml | 89 +- man/sd_bus_set_close_on_exit.xml | 29 +- man/sd_bus_set_connected_signal.xml | 33 +- man/sd_bus_set_description.xml | 81 +- man/sd_bus_set_sender.xml | 42 +- man/sd_bus_set_watch_bind.xml | 33 +- man/sd_bus_slot_ref.xml | 2 +- man/sd_bus_slot_set_description.xml | 46 +- man/sd_bus_slot_set_destroy_callback.xml | 35 +- man/sd_bus_slot_set_floating.xml | 47 +- man/sd_bus_slot_set_userdata.xml | 2 +- man/sd_bus_track_add_name.xml | 49 +- man/sd_bus_track_new.xml | 52 +- man/sd_bus_wait.xml | 42 +- man/sd_event_add_child.xml | 81 +- man/sd_event_add_defer.xml | 53 +- man/sd_event_add_inotify.xml | 59 +- man/sd_event_add_io.xml | 67 +- man/sd_event_add_signal.xml | 71 +- man/sd_event_add_time.xml | 69 +- man/sd_event_exit.xml | 60 +- man/sd_event_get_fd.xml | 45 +- man/sd_event_new.xml | 59 +- man/sd_event_now.xml | 58 +- man/sd_event_run.xml | 63 +- man/sd_event_set_watchdog.xml | 49 +- man/sd_event_source_get_event.xml | 9 +- man/sd_event_source_get_pending.xml | 75 +- man/sd_event_source_set_description.xml | 67 +- man/sd_event_source_set_destroy_callback.xml | 34 +- man/sd_event_source_set_enabled.xml | 54 +- man/sd_event_source_set_prepare.xml | 73 +- man/sd_event_source_set_priority.xml | 64 +- man/sd_event_source_set_userdata.xml | 9 +- man/sd_event_source_unref.xml | 9 +- man/sd_event_wait.xml | 76 +- man/sd_get_seats.xml | 36 +- man/sd_id128_get_machine.xml | 60 +- man/sd_id128_randomize.xml | 7 +- man/sd_id128_to_string.xml | 7 +- man/sd_is_fifo.xml | 7 +- man/sd_journal_add_match.xml | 7 +- man/sd_journal_enumerate_fields.xml | 7 +- man/sd_journal_get_catalog.xml | 7 +- man/sd_journal_get_cursor.xml | 7 +- man/sd_journal_get_cutoff_realtime_usec.xml | 7 +- man/sd_journal_get_data.xml | 7 +- man/sd_journal_get_fd.xml | 7 +- man/sd_journal_get_realtime_usec.xml | 7 +- man/sd_journal_get_usage.xml | 7 +- man/sd_journal_has_runtime_files.xml | 2 +- man/sd_journal_next.xml | 7 +- man/sd_journal_open.xml | 7 +- man/sd_journal_print.xml | 7 +- man/sd_journal_query_unique.xml | 7 +- man/sd_journal_seek_head.xml | 63 +- man/sd_journal_stream_fd.xml | 7 +- man/sd_listen_fds.xml | 7 +- man/sd_login_monitor_new.xml | 38 +- man/sd_machine_get_class.xml | 52 +- man/sd_notify.xml | 7 +- man/sd_pid_get_owner_uid.xml | 73 +- man/sd_seat_get_active.xml | 72 +- man/sd_session_is_active.xml | 58 +- man/sd_uid_get_state.xml | 76 +- man/sd_watchdog_enabled.xml | 7 +- man/shutdown.xml | 9 +- man/standard-conf.xml | 23 +- man/standard-options.xml | 9 +- man/sysctl.d.xml | 7 +- man/system-only.xml | 16 + man/systemctl.xml | 52 +- man/systemd-analyze.xml | 702 ++- man/systemd-ask-password-console.service.xml | 7 +- man/systemd-ask-password.xml | 7 +- man/systemd-backlight@.service.xml | 7 +- man/systemd-binfmt.service.xml | 7 +- man/systemd-bless-boot-generator.xml | 7 +- man/systemd-bless-boot.service.xml | 7 +- ...systemd-boot-check-no-failures.service.xml | 7 +- man/systemd-boot.xml | 65 +- man/systemd-cat.xml | 7 +- man/systemd-cgls.xml | 7 +- man/systemd-cgtop.xml | 7 +- man/systemd-coredump.xml | 9 +- man/systemd-cryptsetup-generator.xml | 7 +- man/systemd-cryptsetup@.service.xml | 7 +- man/systemd-debug-generator.xml | 7 +- man/systemd-delta.xml | 7 +- man/systemd-detect-virt.xml | 27 +- man/systemd-environment-d-generator.xml | 11 +- man/systemd-escape.xml | 7 +- man/systemd-firstboot.xml | 7 +- man/systemd-fsck@.service.xml | 7 +- man/systemd-fstab-generator.xml | 29 +- man/systemd-getty-generator.xml | 7 +- man/systemd-gpt-auto-generator.xml | 134 +- man/systemd-halt.service.xml | 7 +- man/systemd-hibernate-resume-generator.xml | 7 +- man/systemd-hibernate-resume@.service.xml | 7 +- man/systemd-hostnamed.service.xml | 7 +- man/systemd-hwdb.xml | 7 +- man/systemd-id128.xml | 2 +- man/systemd-importd.service.xml | 7 +- man/systemd-inhibit.xml | 7 +- man/systemd-initctl.service.xml | 7 +- man/systemd-journal-gatewayd.service.xml | 9 +- man/systemd-journal-remote.service.xml | 9 +- man/systemd-journal-upload.service.xml | 9 +- man/systemd-journald.service.xml | 7 +- man/systemd-localed.service.xml | 7 +- man/systemd-logind.service.xml | 7 +- man/systemd-machine-id-commit.service.xml | 3 +- man/systemd-machine-id-setup.xml | 7 +- man/systemd-machined.service.xml | 7 +- man/systemd-makefs@.service.xml | 7 +- man/systemd-modules-load.service.xml | 7 +- man/systemd-mount.xml | 33 +- man/systemd-networkd-wait-online.service.xml | 80 +- man/systemd-networkd.service.xml | 7 +- man/systemd-notify.xml | 7 +- man/systemd-nspawn.xml | 751 +-- man/systemd-path.xml | 7 +- man/systemd-portabled.service.xml | 3 +- man/systemd-quotacheck.service.xml | 7 +- man/systemd-random-seed.service.xml | 7 +- man/systemd-rc-local-generator.xml | 7 +- man/systemd-remount-fs.service.xml | 48 +- man/systemd-resolved.service.xml | 11 +- man/systemd-rfkill.service.xml | 7 +- man/systemd-run-generator.xml | 7 +- man/systemd-run.xml | 21 +- man/systemd-sleep.conf.xml | 9 +- man/systemd-socket-activate.xml | 9 +- man/systemd-socket-proxyd.xml | 8 +- man/systemd-suspend.service.xml | 7 +- man/systemd-sysctl.service.xml | 7 +- man/systemd-system-update-generator.xml | 7 +- man/systemd-system.conf.xml | 7 +- man/systemd-sysusers.xml | 9 +- man/systemd-sysv-generator.xml | 7 +- man/systemd-time-wait-sync.service.xml | 7 +- man/systemd-timedated.service.xml | 7 +- man/systemd-timesyncd.service.xml | 7 +- man/systemd-tmpfiles.xml | 7 +- man/systemd-tty-ask-password-agent.xml | 7 +- man/systemd-udevd.service.xml | 7 +- man/systemd-update-done.service.xml | 7 +- man/systemd-update-utmp.service.xml | 7 +- man/systemd-user-sessions.service.xml | 7 +- man/systemd-vconsole-setup.service.xml | 7 +- man/systemd-veritysetup-generator.xml | 7 +- man/systemd-veritysetup@.service.xml | 7 +- man/systemd-volatile-root.service.xml | 7 +- man/systemd.automount.xml | 7 +- man/systemd.device.xml | 7 +- man/systemd.dnssd.xml | 39 +- man/systemd.environment-generator.xml | 7 +- man/systemd.exec.xml | 297 +- man/systemd.generator.xml | 22 +- man/systemd.journal-fields.xml | 7 +- man/systemd.kill.xml | 39 +- man/systemd.link.xml | 136 +- man/systemd.mount.xml | 15 +- man/systemd.netdev.xml | 238 +- man/systemd.network.xml | 195 +- man/systemd.nspawn.xml | 18 +- man/systemd.offline-updates.xml | 7 +- man/systemd.path.xml | 7 +- man/systemd.preset.xml | 7 +- man/systemd.resource-control.xml | 58 +- man/systemd.scope.xml | 9 +- man/systemd.service.xml | 71 +- man/systemd.slice.xml | 9 +- man/systemd.socket.xml | 7 +- man/systemd.special.xml | 48 +- man/systemd.swap.xml | 7 +- man/systemd.syntax.xml | 5 +- man/systemd.target.xml | 7 +- man/systemd.time.xml | 7 +- man/systemd.timer.xml | 95 +- man/systemd.unit.xml | 197 +- man/systemd.xml | 7 +- man/sysusers.d.xml | 7 +- man/telinit.xml | 7 +- man/threads-aware.xml | 8 +- man/timedatectl.xml | 7 +- man/timesyncd.conf.xml | 7 +- man/tmpfiles.d.xml | 25 +- man/udev.conf.xml | 7 +- man/udev.xml | 28 +- man/udev_device_get_syspath.xml | 7 +- man/udev_device_has_tag.xml | 7 +- man/udev_device_new_from_syspath.xml | 7 +- man/udev_enumerate_add_match_subsystem.xml | 7 +- man/udev_enumerate_new.xml | 7 +- man/udev_enumerate_scan_devices.xml | 7 +- man/udev_list_entry.xml | 7 +- man/udev_monitor_filter_update.xml | 7 +- man/udev_monitor_new_from_netlink.xml | 7 +- man/udev_monitor_receive_device.xml | 7 +- man/udev_new.xml | 7 +- man/udevadm.xml | 29 +- man/user-system-options.xml | 9 +- man/user@.service.xml | 3 +- man/vconsole.conf.xml | 7 +- meson.build | 127 +- meson_options.txt | 4 +- mkosi.build | 4 + po/fr.po | 37 +- po/ja.po | 128 +- po/lt.po | 63 +- po/pl.po | 60 +- rules/60-block.rules | 2 +- rules/99-systemd.rules.in | 2 + semaphoreci/gcc-compilation.sh | 43 +- semaphoreci/setup.sh | 77 +- shell-completion/bash/busctl | 9 +- shell-completion/bash/journalctl | 13 +- shell-completion/bash/systemctl.in | 5 +- shell-completion/bash/systemd-id128 | 74 + shell-completion/zsh/_journalctl | 2 + src/activate/activate.c | 18 +- src/analyze/analyze-security.c | 31 +- src/analyze/analyze-verify.c | 2 +- src/analyze/analyze.c | 183 +- src/backlight/backlight.c | 8 +- src/basic/alloc-util.c | 22 +- src/basic/alloc-util.h | 14 +- src/basic/async.c | 1 + src/basic/btrfs-util.c | 2 +- src/basic/capability-util.c | 120 +- src/basic/capability-util.h | 8 +- src/basic/cgroup-util.c | 14 +- src/basic/cgroup-util.h | 2 +- src/basic/chattr-util.h | 31 + src/basic/conf-files.c | 2 +- src/basic/copy.c | 29 +- src/basic/copy.h | 15 +- src/basic/def.h | 45 +- src/basic/device-nodes.c | 2 +- src/basic/env-file.c | 2 +- src/basic/errno-util.h | 69 + src/basic/fd-util.c | 11 + src/basic/fd-util.h | 12 - src/basic/fileio.c | 108 +- src/basic/fileio.h | 20 +- src/basic/fs-util.c | 104 +- src/basic/fs-util.h | 4 +- src/basic/gcrypt-util.c | 4 +- src/basic/glob-util.c | 2 + src/basic/hashmap.c | 7 +- src/basic/hashmap.h | 2 + src/basic/hexdecoct.c | 84 +- src/basic/hexdecoct.h | 5 +- src/basic/in-addr-util.c | 138 +- src/basic/in-addr-util.h | 4 + src/basic/kbd-util.c | 109 + src/basic/kbd-util.h | 20 + src/basic/limits-util.c | 157 + src/basic/limits-util.h | 10 + src/basic/locale-util.c | 93 - src/basic/locale-util.h | 3 - src/basic/log.c | 45 +- src/basic/log.h | 15 +- src/basic/memory-util.c | 57 + src/basic/memory-util.h | 84 + src/basic/mempool.c | 1 + src/basic/meson.build | 16 +- src/basic/missing_fs.h | 4 + src/basic/missing_network.h | 12 + src/basic/missing_socket.h | 4 + src/basic/mkdir.c | 1 + src/basic/namespace-util.c | 172 + src/basic/namespace-util.h | 9 + src/basic/nulstr-util.c | 17 + src/basic/nulstr-util.h | 13 + src/basic/ordered-set.c | 35 + src/basic/ordered-set.h | 8 + src/basic/path-util.c | 51 +- src/basic/plymouth-util.c | 9 + src/basic/plymouth-util.h | 11 + src/basic/process-util.c | 53 +- src/basic/process-util.h | 4 + src/basic/random-util.c | 11 +- src/basic/refcnt.h | 54 - src/basic/rm-rf.c | 5 + src/basic/rm-rf.h | 2 +- src/basic/selinux-util.c | 4 +- src/basic/sigbus.c | 2 +- src/basic/socket-label.c | 8 +- src/basic/socket-util.c | 72 +- src/basic/socket-util.h | 3 + src/basic/sort-util.c | 27 + src/basic/sort-util.h | 70 + src/basic/special.h | 2 + src/basic/stat-util.c | 46 - src/basic/stat-util.h | 3 +- src/basic/stdio-util.h | 2 +- src/basic/strbuf.c | 2 +- src/basic/string-table.h | 8 +- src/basic/string-util.c | 35 +- src/basic/string-util.h | 43 +- src/basic/strv.c | 3 +- src/basic/strv.h | 2 +- src/basic/terminal-util.c | 56 +- src/basic/time-util.c | 11 +- src/basic/time-util.h | 1 + src/basic/user-util.c | 41 +- src/basic/utf8.c | 38 +- src/basic/utf8.h | 2 +- src/basic/util.c | 337 -- src/basic/util.h | 179 +- src/basic/virt.c | 21 +- src/basic/virt.h | 2 + src/basic/xattr-util.c | 7 +- src/binfmt/binfmt.c | 3 +- src/boot/bless-boot.c | 195 +- src/boot/bootctl.c | 445 +- src/boot/efi/boot.c | 248 +- src/boot/efi/crc32.c | 142 + src/boot/efi/crc32.h | 8 + src/boot/efi/linux.c | 111 +- src/boot/efi/linux.h | 82 +- src/boot/efi/meson.build | 4 +- src/boot/efi/stub.c | 17 +- src/boot/efi/util.c | 14 +- src/boot/efi/util.h | 11 + src/busctl/busctl.c | 172 +- src/cgls/cgls.c | 4 +- src/cgtop/cgtop.c | 139 +- src/core/automount.c | 17 +- src/core/bpf-firewall.c | 1 + src/core/cgroup.c | 71 +- src/core/cgroup.h | 12 +- src/core/chown-recursive.c | 33 +- src/core/chown-recursive.h | 2 +- src/core/dbus-cgroup.c | 26 + src/core/dbus-execute.c | 48 +- src/core/dbus-manager.c | 276 +- src/core/dbus-scope.c | 2 +- src/core/dbus-service.c | 25 +- src/core/dbus-timer.c | 40 +- src/core/dbus-unit.c | 176 +- src/core/dbus-unit.h | 8 +- src/core/dbus.c | 5 +- src/core/device.c | 23 +- src/core/dynamic-user.c | 5 +- src/core/dynamic-user.h | 2 +- src/core/emergency-action.c | 19 +- src/core/emergency-action.h | 6 +- src/core/execute.c | 261 +- src/core/execute.h | 81 +- src/core/ima-setup.c | 4 +- src/core/job.c | 26 + src/core/kill.h | 2 +- src/core/load-fragment-gperf.gperf.m4 | 19 +- src/core/load-fragment.c | 165 +- src/core/loopback-setup.c | 1 + src/core/machine-id-setup.c | 1 + src/core/macros.systemd.in | 45 +- src/core/main.c | 21 +- src/core/manager.c | 93 +- src/core/manager.h | 10 +- src/core/meson.build | 46 +- src/core/mount-setup.c | 2 +- src/core/mount.c | 47 +- src/core/namespace.c | 262 +- src/core/namespace.h | 3 + src/core/path.c | 13 +- src/core/scope.c | 8 +- src/core/selinux-access.c | 7 +- src/core/selinux-setup.c | 1 + src/core/service.c | 92 +- src/core/service.h | 2 +- src/core/show-status.c | 4 + src/core/smack-setup.c | 1 + src/core/socket.c | 116 +- src/core/swap.c | 7 +- src/core/systemd.pc.in | 36 +- src/core/timer.c | 54 +- src/core/timer.h | 2 + src/core/transaction.c | 99 +- src/core/transaction.h | 2 +- src/core/unit.c | 225 +- src/core/unit.h | 11 +- src/coredump/coredump-vacuum.c | 5 +- src/coredump/coredump.c | 4 +- src/coredump/coredumpctl.c | 4 +- src/coredump/stacktrace.c | 2 + src/cryptsetup/cryptsetup-generator.c | 8 +- src/cryptsetup/cryptsetup.c | 41 +- src/debug-generator/debug-generator.c | 3 +- src/delta/delta.c | 2 +- .../environment-d-generator.c | 2 +- src/firstboot/firstboot.c | 5 +- src/fsck/fsck.c | 7 +- src/fstab-generator/fstab-generator.c | 66 +- src/fuzz/fuzz-bus-label.c | 18 + src/fuzz/fuzz-calendarspec.c | 24 + src/fuzz/fuzz-dhcp-server.c | 4 + src/fuzz/fuzz-dhcp6-client.c | 3 + src/fuzz/fuzz-dhcp6-client.options | 2 + src/fuzz/fuzz-dns-packet.c | 1 + src/fuzz/fuzz-env-file.c | 31 + src/fuzz/fuzz-env-file.options | 2 + src/fuzz/fuzz-hostname-util.c | 28 + src/fuzz/fuzz-journald-stream.c | 3 +- src/fuzz/fuzz-journald-stream.options | 2 + src/fuzz/fuzz-lldp.c | 3 + src/fuzz/fuzz-lldp.options | 2 + src/fuzz/fuzz-ndisc-rs.c | 3 + src/fuzz/fuzz-ndisc-rs.options | 2 + src/fuzz/fuzz-nspawn-oci.c | 28 + src/fuzz/fuzz-nspawn-settings.c | 28 + src/fuzz/fuzz-time-util.c | 28 + src/fuzz/fuzz-udev-database.c | 26 + src/fuzz/fuzz-unit-file.c | 1 + src/fuzz/meson.build | 34 + src/gpt-auto-generator/gpt-auto-generator.c | 119 +- .../hibernate-resume-generator.c | 2 +- src/hostname/hostnamed.c | 3 + src/id128/id128.c | 2 +- src/import/curl-util.c | 2 + src/import/curl-util.h | 1 + src/import/export-raw.c | 2 +- src/import/export.c | 7 +- src/import/import-common.c | 13 +- src/import/import-fs.c | 7 +- src/import/import-raw.c | 4 +- src/import/import.c | 13 +- src/import/pull-job.c | 2 + src/import/pull-raw.c | 10 +- src/import/pull-tar.c | 2 +- src/import/pull.c | 7 +- src/import/test-qcow2.c | 5 +- src/initctl/initctl.c | 6 +- src/journal-remote/journal-gatewayd.c | 2 + src/journal-remote/journal-remote-main.c | 41 +- src/journal-remote/journal-remote-parse.c | 6 +- src/journal-remote/journal-remote-write.c | 5 +- src/journal-remote/journal-remote-write.h | 1 + src/journal-remote/journal-remote.c | 56 +- src/journal-remote/journal-upload.c | 2 +- src/journal/catalog.c | 95 +- src/journal/catalog.h | 1 + src/journal/compress.c | 2 + src/journal/fsprg.c | 1 + src/journal/journal-authenticate.c | 2 + src/journal/journal-file.c | 9 +- src/journal/journal-file.h | 3 +- src/journal/journal-send.c | 2 +- src/journal/journal-vacuum.c | 2 +- src/journal/journal-verify.c | 2 +- src/journal/journalctl.c | 150 +- src/journal/journald-context.h | 3 + src/journal/journald-native.c | 1 + src/journal/journald-rate-limit.c | 2 +- src/journal/journald-rate-limit.h | 2 +- src/journal/journald-server.h | 1 + src/journal/journald-stream.c | 5 +- src/journal/journald-wall.h | 2 + src/journal/mmap-cache.c | 3 +- src/journal/sd-journal.c | 1 + src/journal/test-compress-benchmark.c | 3 +- src/journal/test-compress.c | 4 +- src/journal/test-journal-flush.c | 4 + src/journal/test-journal-init.c | 4 +- src/journal/test-journal-interleaving.c | 20 +- src/journal/test-journal-stream.c | 4 +- src/journal/test-journal-verify.c | 4 +- src/journal/test-journal.c | 25 +- src/kernel-install/00-entry-directory.install | 32 + src/kernel-install/50-depmod.install | 20 +- src/kernel-install/90-loaderentry.install | 34 +- src/kernel-install/kernel-install | 51 +- src/kernel-install/meson.build | 3 +- src/libsystemd-network/dhcp-identifier.c | 8 + src/libsystemd-network/dhcp-lease-internal.h | 4 +- src/libsystemd-network/dhcp-network.c | 19 +- src/libsystemd-network/dhcp-option.c | 6 +- src/libsystemd-network/dhcp-server-internal.h | 2 +- src/libsystemd-network/dhcp6-option.c | 2 +- src/libsystemd-network/icmp6-util.c | 12 +- src/libsystemd-network/lldp-neighbor.c | 2 +- src/libsystemd-network/ndisc-internal.h | 1 + src/libsystemd-network/ndisc-router.c | 1 + src/libsystemd-network/network-internal.c | 97 +- src/libsystemd-network/network-internal.h | 17 +- src/libsystemd-network/sd-dhcp-client.c | 26 +- src/libsystemd-network/sd-dhcp-lease.c | 78 +- src/libsystemd-network/sd-dhcp6-client.c | 9 +- src/libsystemd-network/sd-ipv4acd.c | 2 +- src/libsystemd-network/sd-ipv4ll.c | 32 +- src/libsystemd-network/sd-lldp.c | 2 + src/libsystemd-network/sd-ndisc.c | 2 +- src/libsystemd-network/sd-radv.c | 2 +- src/libsystemd-network/test-dhcp-client.c | 7 +- src/libsystemd-network/test-dhcp-option.c | 2 +- src/libsystemd-network/test-dhcp6-client.c | 3 +- src/libsystemd-network/test-lldp.c | 3 +- src/libsystemd-network/test-ndisc-ra.c | 1 + src/libsystemd-network/test-ndisc-rs.c | 1 + src/libsystemd/sd-bus/bus-container.c | 1 + src/libsystemd/sd-bus/bus-control.c | 1 + src/libsystemd/sd-bus/bus-convenience.c | 3 + src/libsystemd/sd-bus/bus-error.c | 1 + src/libsystemd/sd-bus/bus-internal.c | 8 +- src/libsystemd/sd-bus/bus-internal.h | 61 +- src/libsystemd/sd-bus/bus-introspect.c | 27 +- src/libsystemd/sd-bus/bus-kernel.c | 2 +- src/libsystemd/sd-bus/bus-match.c | 1 + src/libsystemd/sd-bus/bus-message.c | 104 +- src/libsystemd/sd-bus/bus-message.h | 14 +- src/libsystemd/sd-bus/bus-objects.c | 190 +- src/libsystemd/sd-bus/bus-objects.h | 2 + src/libsystemd/sd-bus/bus-signature.c | 2 +- src/libsystemd/sd-bus/bus-slot.c | 2 +- src/libsystemd/sd-bus/bus-socket.c | 135 +- src/libsystemd/sd-bus/sd-bus.c | 136 +- src/libsystemd/sd-bus/test-bus-address.c | 9 +- src/libsystemd/sd-bus/test-bus-benchmark.c | 1 + src/libsystemd/sd-bus/test-bus-cleanup.c | 17 +- src/libsystemd/sd-bus/test-bus-introspect.c | 3 +- src/libsystemd/sd-bus/test-bus-match.c | 1 + .../sd-bus/test-bus-queue-ref-cycle.c | 56 + src/libsystemd/sd-bus/test-bus-server.c | 2 +- src/libsystemd/sd-bus/test-bus-vtable.c | 65 + src/libsystemd/sd-bus/test-bus-watch-bind.c | 3 +- .../sd-device/device-enumerator-private.h | 1 + src/libsystemd/sd-device/device-enumerator.c | 74 +- src/libsystemd/sd-device/device-internal.h | 21 +- src/libsystemd/sd-device/device-private.c | 147 +- src/libsystemd/sd-device/device-private.h | 21 + src/libsystemd/sd-device/sd-device.c | 56 +- src/libsystemd/sd-device/test-sd-device.c | 2 +- src/libsystemd/sd-event/sd-event.c | 4 +- src/libsystemd/sd-hwdb/hwdb-util.c | 4 +- src/libsystemd/sd-hwdb/sd-hwdb.c | 34 +- src/libsystemd/sd-id128/sd-id128.c | 45 +- src/libsystemd/sd-login/sd-login.c | 2 +- src/libsystemd/sd-login/test-login.c | 1 + src/libsystemd/sd-netlink/generic-netlink.c | 3 +- src/libsystemd/sd-netlink/local-addresses.c | 1 + src/libsystemd/sd-netlink/netlink-internal.h | 6 +- src/libsystemd/sd-netlink/netlink-message.c | 64 +- src/libsystemd/sd-netlink/netlink-socket.c | 1 - src/libsystemd/sd-netlink/netlink-types.c | 62 +- src/libsystemd/sd-netlink/netlink-util.c | 3 + src/libsystemd/sd-netlink/netlink-util.h | 5 + src/libsystemd/sd-netlink/rtnl-message.c | 1 - src/libsystemd/sd-netlink/sd-netlink.c | 4 +- .../sd-netlink/test-local-addresses.c | 2 + src/libsystemd/sd-network/network-util.c | 14 + src/libsystemd/sd-network/network-util.h | 18 + src/libsystemd/sd-network/sd-network.c | 21 + src/libsystemd/sd-resolve/sd-resolve.c | 5 +- src/libudev/libudev-device.c | 27 +- src/libudev/libudev-enumerate.c | 3 - src/libudev/libudev-list.c | 2 +- src/libudev/libudev-util.c | 2 +- src/locale/keymap-util.c | 30 +- src/locale/localectl.c | 4 +- src/locale/localed.c | 2 + src/locale/test-keymap-util.c | 4 +- src/login/70-uaccess.rules.m4 | 4 + src/login/71-seat.rules.in | 12 + src/login/inhibit.c | 9 +- src/login/loginctl.c | 8 +- src/login/logind-core.c | 11 +- src/login/logind-dbus.c | 749 ++- src/login/logind-inhibit.c | 8 +- src/login/logind-seat.c | 1 + src/login/logind-session-device.c | 20 +- src/login/logind-session.c | 17 +- src/login/logind-user.c | 6 +- src/login/logind.c | 130 +- src/login/logind.h | 1 + src/login/org.freedesktop.login1.conf | 24 + src/login/org.freedesktop.login1.policy | 35 +- src/login/pam_systemd.c | 28 +- src/login/test-inhibit.c | 3 +- src/login/user-runtime-dir.c | 1 + src/machine/machine-dbus.c | 1 + src/machine/machine.c | 1 + src/machine/machine.h | 1 + src/machine/machinectl.c | 59 +- src/machine/machined.c | 2 + src/mount/mount-tool.c | 5 +- src/network/fuzz-netdev-parser.c | 2 +- src/network/fuzz-network-parser.c | 5 +- src/network/fuzz-network-parser.options | 2 + src/network/meson.build | 2 + src/network/netdev/bond.c | 38 +- src/network/netdev/fou-tunnel.c | 115 +- src/network/netdev/fou-tunnel.h | 1 + src/network/netdev/geneve.c | 9 +- src/network/netdev/l2tp-tunnel.c | 734 +++ src/network/netdev/l2tp-tunnel.h | 77 + src/network/netdev/netdev-gperf.gperf | 31 +- src/network/netdev/netdev.c | 116 +- src/network/netdev/netdev.h | 21 +- src/network/netdev/tunnel.c | 438 +- src/network/netdev/tunnel.h | 2 +- src/network/netdev/vxlan.c | 8 +- src/network/netdev/wireguard.c | 789 +-- src/network/netdev/wireguard.h | 35 +- src/network/networkctl.c | 98 +- src/network/networkd-address-label.c | 4 +- src/network/networkd-address-label.h | 3 +- src/network/networkd-address-pool.c | 33 +- src/network/networkd-address-pool.h | 1 - src/network/networkd-address.c | 181 +- src/network/networkd-address.h | 5 +- src/network/networkd-dhcp4.c | 62 +- src/network/networkd-dhcp6.c | 34 +- src/network/networkd-fdb.c | 6 +- src/network/networkd-fdb.h | 4 +- src/network/networkd-ipv4ll.c | 49 +- src/network/networkd-ipv6-proxy-ndp.c | 17 +- src/network/networkd-ipv6-proxy-ndp.h | 7 +- src/network/networkd-link.c | 489 +- src/network/networkd-link.h | 49 +- src/network/networkd-manager.c | 131 +- src/network/networkd-manager.h | 6 +- src/network/networkd-ndisc.c | 39 +- src/network/networkd-ndisc.h | 1 + src/network/networkd-neighbor.c | 21 +- src/network/networkd-neighbor.h | 3 +- src/network/networkd-network-gperf.gperf | 43 +- src/network/networkd-network.c | 746 +-- src/network/networkd-network.h | 43 +- src/network/networkd-radv.c | 19 +- src/network/networkd-radv.h | 4 +- src/network/networkd-route.c | 288 +- src/network/networkd-route.h | 8 +- src/network/networkd-routing-policy-rule.c | 93 +- src/network/networkd-routing-policy-rule.h | 3 +- src/network/networkd-util.c | 36 + src/network/networkd-util.h | 41 +- src/network/networkd.c | 3 + src/network/test-networkd-conf.c | 3 + src/network/wait-online/link.c | 74 +- src/network/wait-online/link.h | 6 +- src/network/wait-online/manager.c | 127 +- src/network/wait-online/manager.h | 15 +- src/network/wait-online/wait-online.c | 106 +- src/nspawn/meson.build | 2 + src/nspawn/nspawn-cgroup.c | 3 +- src/nspawn/nspawn-gperf.gperf | 1 + src/nspawn/nspawn-mount.c | 334 +- src/nspawn/nspawn-mount.h | 14 +- src/nspawn/nspawn-oci.c | 2269 +++++++++ src/nspawn/nspawn-oci.h | 6 + src/nspawn/nspawn-patch-uid.c | 2 +- src/nspawn/nspawn-register.c | 17 +- src/nspawn/nspawn-register.h | 4 +- src/nspawn/nspawn-seccomp.c | 1 + src/nspawn/nspawn-settings.c | 157 +- src/nspawn/nspawn-settings.h | 72 +- src/nspawn/nspawn-setuid.c | 53 +- src/nspawn/nspawn-setuid.h | 3 +- src/nspawn/nspawn-stub-pid1.c | 2 +- src/nspawn/nspawn.c | 978 +++- src/nss-myhostname/nss-myhostname.c | 34 +- src/nss-mymachines/nss-mymachines.c | 8 +- src/nss-resolve/nss-resolve.c | 36 +- src/nss-systemd/nss-systemd.c | 1 + src/partition/growfs.c | 1 + src/portable/org.freedesktop.portable1.conf | 6 +- src/portable/portable.c | 4 +- src/portable/portabled-bus.c | 2 +- src/portable/portabled-image-bus.c | 7 +- src/portable/portabled.c | 3 + src/quotacheck/quotacheck.c | 2 + src/random-seed/random-seed.c | 2 +- src/remount-fs/remount-fs.c | 74 +- src/reply-password/reply-password.c | 1 + src/resolve/resolvectl.c | 7 +- src/resolve/resolved-bus.c | 3 +- src/resolve/resolved-dns-answer.c | 53 +- src/resolve/resolved-dns-dnssec.c | 80 +- src/resolve/resolved-dns-packet.c | 24 +- src/resolve/resolved-dns-packet.h | 6 +- src/resolve/resolved-dns-query.c | 6 +- src/resolve/resolved-dns-question.c | 22 +- src/resolve/resolved-dns-question.h | 1 + src/resolve/resolved-dns-rr.c | 52 +- src/resolve/resolved-dns-rr.h | 3 + src/resolve/resolved-dns-stream.c | 47 +- src/resolve/resolved-dns-stream.h | 14 +- src/resolve/resolved-dns-stub.c | 115 +- src/resolve/resolved-dns-transaction.c | 18 +- src/resolve/resolved-dns-trust-anchor.c | 4 +- src/resolve/resolved-dnssd.c | 16 +- src/resolve/resolved-etc-hosts.c | 4 + src/resolve/resolved-llmnr.c | 7 +- src/resolve/resolved-manager.c | 4 + src/resolve/resolved-manager.h | 2 +- src/resolve/resolved-mdns.c | 1 + src/resolve/resolved-resolv-conf.c | 3 + src/resolve/resolved.c | 4 + src/resolve/test-resolved-etc-hosts.c | 5 +- src/resolve/test-resolved-packet.c | 6 +- src/rfkill/rfkill.c | 8 +- src/run-generator/run-generator.c | 2 + src/run/run.c | 50 +- src/shared/acl-util.c | 2 + src/shared/ask-password-api.c | 33 +- src/shared/base-filesystem.c | 29 +- src/shared/bitmap.c | 1 + src/shared/bootspec.c | 1153 ++++- src/shared/bootspec.h | 39 +- src/shared/bpf-program.c | 2 +- src/shared/bus-unit-procs.c | 411 ++ src/shared/bus-unit-procs.h | 8 + src/shared/bus-unit-util.c | 766 +-- src/shared/bus-unit-util.h | 16 +- src/shared/bus-util.c | 62 +- src/shared/bus-util.h | 4 +- src/shared/bus-wait-for-jobs.c | 337 ++ src/shared/bus-wait-for-jobs.h | 15 + src/shared/calendarspec.c | 186 +- src/shared/cgroup-show.c | 1 + src/shared/clock-util.c | 2 +- src/shared/condition.c | 243 +- src/shared/condition.h | 9 +- src/shared/conf-parser.c | 2 + src/shared/conf-parser.h | 1 + src/shared/crypt-util.h | 8 + src/shared/dev-setup.c | 2 +- src/shared/dissect-image.c | 105 +- src/shared/dissect-image.h | 1 + src/shared/efivars.c | 24 +- src/shared/efivars.h | 4 +- src/shared/exec-util.c | 53 +- src/shared/exec-util.h | 9 +- src/shared/fdset.c | 24 +- src/shared/fdset.h | 2 + src/shared/format-table.c | 2 + src/shared/fstab-util.c | 2 +- src/shared/fstab-util.h | 4 +- src/shared/generator.c | 27 +- src/shared/generator.h | 4 +- src/shared/gpt.h | 1 + src/shared/import-util.c | 2 +- src/shared/install.c | 51 +- src/shared/json.c | 78 +- src/shared/json.h | 9 +- src/shared/libmount-util.h | 10 + src/shared/log-link.h | 37 + src/shared/logs-show.c | 1 + src/shared/machine-image.c | 15 +- src/shared/main-func.h | 6 +- src/shared/meson.build | 7 + src/shared/mount-util.c | 37 +- src/shared/mount-util.h | 4 +- src/shared/pager.c | 1 + src/shared/pe-header.h | 59 + src/shared/pretty-print.c | 78 +- src/shared/reboot-util.c | 17 +- src/shared/reboot-util.h | 3 +- src/shared/seccomp-util.c | 222 +- src/shared/seccomp-util.h | 16 +- src/shared/serialize.h | 1 + src/shared/sleep-config.c | 7 +- src/shared/specifier.c | 1 + src/shared/sysctl-util.c | 38 + src/shared/sysctl-util.h | 22 + src/shared/udev-util.c | 25 +- src/shared/udev-util.h | 3 + src/shared/uid-range.c | 2 +- src/shared/utmp-wtmp.c | 2 +- src/shared/volatile-util.c | 20 +- src/shared/volatile-util.h | 1 + src/shutdown/meson.build | 5 + src/{core => shutdown}/shutdown.c | 18 +- src/{core => shutdown}/umount.c | 50 +- src/{core => shutdown}/umount.h | 0 src/sleep/sleep.c | 9 +- src/socket-proxy/socket-proxyd.c | 11 +- src/stdio-bridge/stdio-bridge.c | 37 +- src/sysctl/sysctl.c | 3 +- src/systemctl/systemctl.c | 1274 +++-- src/systemctl/sysv-compat.c | 102 + src/systemctl/sysv-compat.h | 33 + src/systemd/sd-bus-vtable.h | 27 +- src/systemd/sd-bus.h | 6 + src/systemd/sd-dhcp-lease.h | 2 +- src/systemd/sd-event.h | 2 +- src/systemd/sd-netlink.h | 8 +- src/systemd/sd-network.h | 1 + src/sysusers/sysusers.c | 263 +- src/test/meson.build | 27 +- src/test/test-alloc-util.c | 22 +- src/test/test-async.c | 1 + src/test/test-barrier.c | 1 + src/test/test-boot-timestamps.c | 3 + src/test/test-cap-list.c | 1 + src/test/test-capability.c | 21 + src/test/test-cgroup-cpu.c | 38 + src/test/test-chown-rec.c | 29 +- src/test/test-condition.c | 129 +- src/test/test-copy.c | 8 +- src/test/test-daemon.c | 1 + src/test/test-date.c | 2 +- src/test/test-engine.c | 20 +- src/test/test-exec-util.c | 48 +- src/test/test-execute.c | 363 +- src/test/test-fd-util.c | 2 +- src/test/test-fileio.c | 14 +- src/test/test-fs-util.c | 24 + src/test/test-fstab-util.c | 3 +- src/test/test-hashmap-plain.c | 3 +- src/test/test-hostname-util.c | 2 + src/test/test-id128.c | 3 + src/test/test-in-addr-util.c | 152 +- src/test/test-install-root.c | 2 + src/test/test-json.c | 13 +- src/test/test-libmount.c | 118 + src/test/test-libudev.c | 124 +- src/test/test-locale-util.c | 2 + src/test/test-log.c | 9 +- src/test/test-mountpoint-util.c | 14 +- src/test/test-namespace.c | 1 + src/test/test-netlink-manual.c | 2 + src/test/test-nss.c | 16 +- src/test/test-ordered-set.c | 121 + src/test/test-path-util.c | 9 +- src/test/test-pretty-print.c | 3 + src/test/test-prioq.c | 2 +- src/test/test-proc-cmdline.c | 18 +- src/test/test-process-util.c | 3 +- src/test/test-replace-var.c | 3 +- src/test/test-rlimit-util.c | 2 +- src/test/test-seccomp.c | 235 +- src/test/test-set.c | 7 + src/test/test-sigbus.c | 4 +- src/test/test-siphash24.c | 2 +- src/test/test-sleep.c | 4 + src/test/test-socket-util.c | 32 +- src/test/test-stat-util.c | 1 + src/test/test-string-util.c | 1 + src/test/test-strv.c | 2 +- src/test/test-tables.c | 4 +- src/test/test-terminal-util.c | 25 +- src/test/test-time-util.c | 30 +- src/test/test-unaligned.c | 2 +- src/test/test-unit-file.c | 3 +- src/test/test-unit-name.c | 3 +- src/test/test-user-util.c | 4 +- src/test/test-utf8.c | 20 +- src/test/test-util.c | 3 +- src/test/test-verbs.c | 2 + src/test/test-watch-pid.c | 12 +- src/timedate/timedatectl.c | 2 +- src/timedate/timedated.c | 8 +- src/timesync/timesyncd-manager.c | 1 + src/timesync/timesyncd.c | 3 + src/tmpfiles/tmpfiles.c | 99 +- .../tty-ask-password-agent.c | 71 +- src/udev/ata_id/ata_id.c | 2 +- src/udev/cdrom_id/cdrom_id.c | 4 +- src/udev/meson.build | 12 + src/udev/net/ethtool-util.c | 8 +- src/udev/net/fuzz-link-parser.c | 28 + src/udev/net/fuzz-link-parser.options | 2 + src/udev/net/link-config-gperf.gperf | 66 +- src/udev/net/link-config.c | 201 +- src/udev/net/link-config.h | 30 +- src/udev/scsi_id/scsi_serial.c | 2 +- src/udev/udev-builtin-usb_id.c | 19 +- src/udev/udev-builtin.c | 2 +- src/udev/udev-ctrl.c | 531 +- src/udev/udev-ctrl.h | 93 +- src/udev/udev-event.c | 129 +- src/udev/udev-node.c | 6 +- src/udev/udev-node.h | 2 +- src/udev/udev-rules.c | 322 +- src/udev/udev.h | 4 +- src/udev/udevadm-control.c | 74 +- src/udev/udevadm-info.c | 11 +- src/udev/udevadm-monitor.c | 10 +- src/udev/udevadm-settle.c | 9 +- src/udev/udevadm-test.c | 12 +- src/udev/udevadm-trigger.c | 30 +- src/udev/udevadm.c | 2 +- src/udev/udevd.c | 196 +- src/udev/v4l_id/v4l_id.c | 21 +- src/update-done/update-done.c | 6 +- src/update-utmp/update-utmp.c | 2 + src/user-sessions/user-sessions.c | 4 +- src/vconsole/vconsole-setup.c | 2 + src/volatile-root/volatile-root.c | 120 +- test/README.testsuite | 33 +- test/TEST-02-CRYPTSETUP/test.sh | 2 +- test/TEST-03-JOBS/test-jobs.sh | 7 + test/TEST-04-JOURNAL/test-journal.sh | 12 + test/TEST-11-ISSUE-3166/test.sh | 2 + test/TEST-20-MAINPIDGAMES/testsuite.sh | 6 +- test/TEST-22-TMPFILES/test-08.sh | 32 + test/TEST-23-TYPE-EXEC/testsuite.sh | 6 +- test/TEST-24-UNIT-TESTS/test.sh | 2 + test/TEST-24-UNIT-TESTS/testsuite.sh | 99 +- test/TEST-27-STDOUTFILE/testsuite.sh | 6 +- test/TEST-29-UDEV-ID_RENAMING/Makefile | 1 + test/TEST-29-UDEV-ID_RENAMING/test.sh | 49 + test/TEST-29-UDEV-ID_RENAMING/testsuite.sh | 45 + test/TEST-30-ONCLOCKCHANGE/Makefile | 1 + test/TEST-30-ONCLOCKCHANGE/test.sh | 60 + test/TEST-30-ONCLOCKCHANGE/testsuite.sh | 33 + test/TEST-31-DEVICE-ENUMERATION/Makefile | 1 + test/TEST-31-DEVICE-ENUMERATION/test.sh | 49 + test/TEST-31-DEVICE-ENUMERATION/testsuite.sh | 12 + test/fuzz/fuzz-bus-message/oss-fuzz-14016 | Bin 0 -> 49343 bytes test/fuzz/fuzz-calendarspec/oss-fuzz-14108 | 1 + test/fuzz/fuzz-dns-packet/oss-fuzz-13422 | Bin 0 -> 36510 bytes test/fuzz/fuzz-env-file/simple-env-file | 5 + test/fuzz/fuzz-journal-remote/oss-fuzz-9341 | Bin 0 -> 1153 bytes test/fuzz/fuzz-link-parser/99-default.link | 12 + .../fuzz/fuzz-link-parser/advertise-segv.link | 2 + .../fuzz-link-parser/condition-memleak.link | 7 + test/fuzz/fuzz-link-parser/directives.link | 35 + test/fuzz/fuzz-link-parser/oss-fuzz-13878 | 4 + test/fuzz/fuzz-link-parser/oss-fuzz-13882 | Bin 0 -> 2015 bytes .../fuzz/fuzz-netdev-parser/directives.netdev | 18 + test/fuzz/fuzz-netdev-parser/oss-fuzz-13719 | 6 + test/fuzz/fuzz-netdev-parser/oss-fuzz-13884 | Bin 0 -> 449 bytes test/fuzz/fuzz-netdev-parser/oss-fuzz-13886 | Bin 0 -> 47998 bytes test/fuzz/fuzz-netdev-parser/oss-fuzz-14157 | 5 + test/fuzz/fuzz-netdev-parser/oss-fuzz-14158 | 5 + .../fuzz-network-parser/directives.network | 9 + test/fuzz/fuzz-network-parser/oss-fuzz-13059 | Bin 0 -> 6558 bytes test/fuzz/fuzz-network-parser/oss-fuzz-13354 | Bin 0 -> 918848 bytes test/fuzz/fuzz-network-parser/oss-fuzz-13433 | Bin 0 -> 29 bytes test/fuzz/fuzz-network-parser/oss-fuzz-13888 | Bin 0 -> 56 bytes test/fuzz/fuzz-nspawn-oci/basic.json | 141 + ...h-bffbd2085d4e95c47e9749b3f4a2dbc0580c20d3 | 5 + ...h-db0595479ee2e625fa5419a821009b5eb4d809b7 | 92 + test/fuzz/fuzz-nspawn-settings/basic-config | 36 + ...k-4ff0e2498f596a77ea68d185c61e9e9ff9bb657f | Bin 0 -> 133 bytes test/fuzz/fuzz-nspawn-settings/oss-fuzz-13691 | 2 + test/fuzz/fuzz-udev-database/sample.txt | 500 ++ test/fuzz/fuzz-unit-file/directives.service | 3 +- test/fuzz/fuzz-unit-file/oss-fuzz-13125 | 10 + .../fuzz-unit-file/systemd-machined.service | 18 + test/fuzz/meson.build | 6 +- test/meson.build | 4 +- test/networkd-test.py | 106 +- .../exec-environment-no-substitute.service | 8 + ...ice => exec-inaccessiblepaths-sys.service} | 4 +- ...home-tmpfs-vs-protectsystem-strict.service | 9 + test/test-functions | 55 +- test/test-network/conf/11-dummy.network | 5 + .../conf/15-name-conflict-test.netdev | 4 + test/test-network/conf/21-macvlan.netdev | 1 + test/test-network/conf/21-vlan-test1.network | 2 + .../21-vlan-test1.network.d/override.conf | 5 + .../conf/21-vlan.netdev.d/override.conf | 3 + test/test-network/conf/21-vlan.network | 6 +- .../conf/21-vlan.network.d/override.conf | 2 - ...dress-preferred-lifetime-zero-ipv6.network | 27 + .../25-address-section-miscellaneous.network | 10 - .../conf/25-address-section.network | 18 - .../conf/25-address-static.network | 53 + .../test-network/conf/25-bind-carrier.network | 8 + test/test-network/conf/25-bridge.network | 5 + .../conf/25-erspan-tunnel-local-any.netdev | 10 + .../test-network/conf/25-erspan-tunnel.netdev | 3 +- test/test-network/conf/25-fou-gre.netdev | 14 + test/test-network/conf/25-fou-gretap.netdev | 13 + test/test-network/conf/25-fou-ipip.netdev | 12 + .../conf/25-fou-ipproto-gre.netdev | 8 + .../conf/25-fou-ipproto-ipip.netdev | 8 + test/test-network/conf/25-fou-sit.netdev | 11 + .../conf/25-gre-tunnel-local-any.netdev | 9 + .../conf/25-gre-tunnel-remote-any.netdev | 9 + test/test-network/conf/25-gre-tunnel.netdev | 3 + .../conf/25-gretap-tunnel-local-any.netdev | 9 + .../test-network/conf/25-gretap-tunnel.netdev | 2 + .../conf/25-ip6gre-tunnel-local-any.netdev | 7 + .../conf/25-ip6gre-tunnel-remote-any.netdev | 7 + .../test-network/conf/25-ip6gre-tunnel.netdev | 4 +- .../conf/25-ip6gretap-tunnel-local-any.netdev | 7 + .../conf/25-ip6gretap-tunnel.netdev | 7 + .../conf/25-ip6tnl-tunnel-local-any.netdev | 8 + .../conf/25-ip6tnl-tunnel-remote-any.netdev | 8 + .../conf/25-ipip-tunnel-local-any.netdev | 8 + .../conf/25-ipip-tunnel-remote-any.netdev | 8 + test/test-network/conf/25-l2tp-dummy.network | 7 + test/test-network/conf/25-l2tp-ip.netdev | 20 + test/test-network/conf/25-l2tp-udp.netdev | 25 + .../conf/25-link-local-addressing-yes.network | 1 + .../conf/25-route-gateway-on-link.network | 13 - .../conf/25-route-gateway.network | 14 - .../conf/25-route-reverse-order.network | 15 - .../conf/25-route-section.network | 8 - .../test-network/conf/25-route-static.network | 46 + .../conf/25-route-tcp-window-settings.network | 10 - test/test-network/conf/25-route-type.network | 14 - .../conf/25-sit-tunnel-local-any.netdev | 7 + .../conf/25-sit-tunnel-remote-any.netdev | 7 + .../conf/25-sysctl-disable-ipv6.network | 6 + test/test-network/conf/25-sysctl.network | 1 + test/test-network/conf/25-vrf.network | 2 + .../conf/25-vti-tunnel-local-any.netdev | 7 + .../conf/25-vti-tunnel-remote-any.netdev | 7 + .../conf/25-vti6-tunnel-local-any.netdev | 7 + .../conf/25-vti6-tunnel-remote-any.netdev | 7 + .../conf/25-wireguard-23-peers.netdev | 4 +- .../conf/25-wireguard-preshared-key.txt | 3 + .../conf/25-wireguard-private-key.txt | 6 + .../conf/25-wireguard.netdev.d/peer.conf | 5 + test/test-network/conf/25-wireguard.network | 2 + .../conf/26-bridge-slave-interface-1.network | 3 + test/test-network/conf/bond-slave.network | 12 + test/test-network/conf/bond99.network | 6 + .../conf/bridge99-ignore-carrier-loss.network | 12 + test/test-network/conf/bridge99.network | 1 + ...hcp-client-gateway-onlink-implicit.network | 13 + .../dhcp-client-ipv4-dhcp-settings.network | 1 + .../test-network/conf/dhcp-client-vrf.network | 8 + test/test-network/conf/dhcp-server.network | 1 + test/test-network/conf/erspan.network | 6 + test/test-network/conf/gretap.network | 1 + test/test-network/conf/gretun.network | 2 + test/test-network/conf/ip6gretap.network | 1 + test/test-network/conf/ip6gretun.network | 7 + test/test-network/conf/ip6tnl.network | 2 + test/test-network/conf/ipip.network | 2 + .../conf/routing-policy-rule-dummy98.network | 10 + ...work => routing-policy-rule-test1.network} | 0 test/test-network/conf/sit.network | 2 + test/test-network/conf/test-static.network | 6 - test/test-network/conf/vlan6.netdev | 7 + test/test-network/conf/vlan6.network | 6 + test/test-network/conf/vti.network | 2 + test/test-network/conf/vti6.network | 2 + test/test-network/systemd-networkd-tests.py | 1202 ++++- test/udev-test.pl | 170 +- tools/check-directives.sh | 12 +- tools/meson-hwdb-update.sh | 4 +- travis-ci/managers/debian.sh | 2 +- units/dev-mqueue.mount | 1 + units/meson.build | 35 +- units/proc-sys-fs-binfmt_misc.mount | 1 + units/sys-fs-fuse-connections.mount | 1 + units/sys-kernel-config.mount | 1 + units/sys-kernel-debug.mount | 1 + units/system-update-cleanup.service | 2 +- units/systemd-coredump@.service.in | 2 + units/systemd-hostnamed.service.in | 1 + units/systemd-importd.service.in | 1 + .../systemd-journal-catalog-update.service.in | 2 +- units/systemd-journal-gatewayd.service.in | 2 +- units/systemd-journal-remote.service.in | 2 + units/systemd-journal-upload.service.in | 2 +- units/systemd-journald.service.in | 1 + units/systemd-localed.service.in | 2 + units/systemd-logind.service.in | 12 +- units/systemd-machined.service.in | 1 + units/systemd-networkd.service.in | 1 + units/systemd-portabled.service.in | 1 + units/systemd-remount-fs.service.in | 1 - units/systemd-resolved.service.in | 1 + units/systemd-timedated.service.in | 2 + units/systemd-timesyncd.service.in | 6 +- units/systemd-tmpfiles-clean.service.in | 2 +- units/systemd-udevd.service.in | 4 +- units/time-set.target | 13 + units/time-sync.target | 2 + units/tmp.mount | 3 +- units/usb-gadget.target | 12 + units/user/default.target | 2 +- units/user@.service.in | 1 + 1206 files changed, 38193 insertions(+), 17322 deletions(-) create mode 100644 .ctags create mode 100644 .lgtm/cpp-queries/PotentiallyDangerousFunction.ql delete mode 100644 .lgtm/cpp-queries/fgets.ql create mode 100644 coccinelle/empty-or-dash.cocci create mode 100644 coccinelle/log-json.cocci create mode 100644 docs/TEMPORARY_DIRECTORIES.md create mode 100644 man/system-only.xml create mode 100644 shell-completion/bash/systemd-id128 create mode 100644 src/basic/errno-util.h create mode 100644 src/basic/kbd-util.c create mode 100644 src/basic/kbd-util.h create mode 100644 src/basic/limits-util.c create mode 100644 src/basic/limits-util.h create mode 100644 src/basic/memory-util.c create mode 100644 src/basic/memory-util.h create mode 100644 src/basic/namespace-util.c create mode 100644 src/basic/namespace-util.h create mode 100644 src/basic/nulstr-util.c create mode 100644 src/basic/nulstr-util.h create mode 100644 src/basic/plymouth-util.c create mode 100644 src/basic/plymouth-util.h delete mode 100644 src/basic/refcnt.h create mode 100644 src/basic/sort-util.c create mode 100644 src/basic/sort-util.h create mode 100644 src/boot/efi/crc32.c create mode 100644 src/boot/efi/crc32.h create mode 100644 src/fuzz/fuzz-bus-label.c create mode 100644 src/fuzz/fuzz-calendarspec.c create mode 100644 src/fuzz/fuzz-dhcp6-client.options create mode 100644 src/fuzz/fuzz-env-file.c create mode 100644 src/fuzz/fuzz-env-file.options create mode 100644 src/fuzz/fuzz-hostname-util.c create mode 100644 src/fuzz/fuzz-journald-stream.options create mode 100644 src/fuzz/fuzz-lldp.options create mode 100644 src/fuzz/fuzz-ndisc-rs.options create mode 100644 src/fuzz/fuzz-nspawn-oci.c create mode 100644 src/fuzz/fuzz-nspawn-settings.c create mode 100644 src/fuzz/fuzz-time-util.c create mode 100644 src/fuzz/fuzz-udev-database.c create mode 100644 src/kernel-install/00-entry-directory.install create mode 100644 src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c create mode 100644 src/network/fuzz-network-parser.options create mode 100644 src/network/netdev/l2tp-tunnel.c create mode 100644 src/network/netdev/l2tp-tunnel.h create mode 100644 src/nspawn/nspawn-oci.c create mode 100644 src/nspawn/nspawn-oci.h create mode 100644 src/shared/bus-unit-procs.c create mode 100644 src/shared/bus-unit-procs.h create mode 100644 src/shared/bus-wait-for-jobs.c create mode 100644 src/shared/bus-wait-for-jobs.h create mode 100644 src/shared/libmount-util.h create mode 100644 src/shared/log-link.h create mode 100644 src/shared/pe-header.h create mode 100644 src/shutdown/meson.build rename src/{core => shutdown}/shutdown.c (97%) rename src/{core => shutdown}/umount.c (95%) rename src/{core => shutdown}/umount.h (100%) create mode 100644 src/systemctl/sysv-compat.c create mode 100644 src/systemctl/sysv-compat.h create mode 100644 src/test/test-cgroup-cpu.c create mode 100644 src/test/test-libmount.c create mode 100644 src/test/test-ordered-set.c create mode 100644 src/udev/net/fuzz-link-parser.c create mode 100644 src/udev/net/fuzz-link-parser.options create mode 100755 test/TEST-22-TMPFILES/test-08.sh create mode 120000 test/TEST-29-UDEV-ID_RENAMING/Makefile create mode 100755 test/TEST-29-UDEV-ID_RENAMING/test.sh create mode 100755 test/TEST-29-UDEV-ID_RENAMING/testsuite.sh create mode 120000 test/TEST-30-ONCLOCKCHANGE/Makefile create mode 100755 test/TEST-30-ONCLOCKCHANGE/test.sh create mode 100755 test/TEST-30-ONCLOCKCHANGE/testsuite.sh create mode 120000 test/TEST-31-DEVICE-ENUMERATION/Makefile create mode 100755 test/TEST-31-DEVICE-ENUMERATION/test.sh create mode 100755 test/TEST-31-DEVICE-ENUMERATION/testsuite.sh create mode 100644 test/fuzz/fuzz-bus-message/oss-fuzz-14016 create mode 100644 test/fuzz/fuzz-calendarspec/oss-fuzz-14108 create mode 100644 test/fuzz/fuzz-dns-packet/oss-fuzz-13422 create mode 100644 test/fuzz/fuzz-env-file/simple-env-file create mode 100644 test/fuzz/fuzz-journal-remote/oss-fuzz-9341 create mode 100644 test/fuzz/fuzz-link-parser/99-default.link create mode 100644 test/fuzz/fuzz-link-parser/advertise-segv.link create mode 100644 test/fuzz/fuzz-link-parser/condition-memleak.link create mode 100644 test/fuzz/fuzz-link-parser/directives.link create mode 100644 test/fuzz/fuzz-link-parser/oss-fuzz-13878 create mode 100644 test/fuzz/fuzz-link-parser/oss-fuzz-13882 create mode 100644 test/fuzz/fuzz-netdev-parser/oss-fuzz-13719 create mode 100644 test/fuzz/fuzz-netdev-parser/oss-fuzz-13884 create mode 100644 test/fuzz/fuzz-netdev-parser/oss-fuzz-13886 create mode 100644 test/fuzz/fuzz-netdev-parser/oss-fuzz-14157 create mode 100644 test/fuzz/fuzz-netdev-parser/oss-fuzz-14158 create mode 100644 test/fuzz/fuzz-network-parser/oss-fuzz-13059 create mode 100644 test/fuzz/fuzz-network-parser/oss-fuzz-13354 create mode 100644 test/fuzz/fuzz-network-parser/oss-fuzz-13433 create mode 100644 test/fuzz/fuzz-network-parser/oss-fuzz-13888 create mode 100644 test/fuzz/fuzz-nspawn-oci/basic.json create mode 100644 test/fuzz/fuzz-nspawn-oci/crash-bffbd2085d4e95c47e9749b3f4a2dbc0580c20d3 create mode 100644 test/fuzz/fuzz-nspawn-oci/crash-db0595479ee2e625fa5419a821009b5eb4d809b7 create mode 100644 test/fuzz/fuzz-nspawn-settings/basic-config create mode 100644 test/fuzz/fuzz-nspawn-settings/leak-4ff0e2498f596a77ea68d185c61e9e9ff9bb657f create mode 100644 test/fuzz/fuzz-nspawn-settings/oss-fuzz-13691 create mode 100644 test/fuzz/fuzz-udev-database/sample.txt create mode 100644 test/fuzz/fuzz-unit-file/oss-fuzz-13125 create mode 100644 test/test-execute/exec-environment-no-substitute.service rename test/test-execute/{exec-inaccessiblepaths-proc.service => exec-inaccessiblepaths-sys.service} (63%) create mode 100644 test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service create mode 100644 test/test-network/conf/11-dummy.network create mode 100644 test/test-network/conf/15-name-conflict-test.netdev create mode 100644 test/test-network/conf/21-vlan-test1.network create mode 100644 test/test-network/conf/21-vlan-test1.network.d/override.conf delete mode 100644 test/test-network/conf/21-vlan.network.d/override.conf create mode 100644 test/test-network/conf/25-address-preferred-lifetime-zero-ipv6.network delete mode 100644 test/test-network/conf/25-address-section-miscellaneous.network delete mode 100644 test/test-network/conf/25-address-section.network create mode 100644 test/test-network/conf/25-address-static.network create mode 100644 test/test-network/conf/25-bind-carrier.network create mode 100644 test/test-network/conf/25-bridge.network create mode 100644 test/test-network/conf/25-erspan-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-fou-gre.netdev create mode 100644 test/test-network/conf/25-fou-gretap.netdev create mode 100644 test/test-network/conf/25-fou-ipip.netdev create mode 100644 test/test-network/conf/25-fou-ipproto-gre.netdev create mode 100644 test/test-network/conf/25-fou-ipproto-ipip.netdev create mode 100644 test/test-network/conf/25-fou-sit.netdev create mode 100644 test/test-network/conf/25-gre-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-gre-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-gretap-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-ip6gre-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-ip6gre-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-ip6gretap-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-ip6gretap-tunnel.netdev create mode 100644 test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-ipip-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-ipip-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-l2tp-dummy.network create mode 100644 test/test-network/conf/25-l2tp-ip.netdev create mode 100644 test/test-network/conf/25-l2tp-udp.netdev delete mode 100644 test/test-network/conf/25-route-gateway-on-link.network delete mode 100644 test/test-network/conf/25-route-gateway.network delete mode 100644 test/test-network/conf/25-route-reverse-order.network delete mode 100644 test/test-network/conf/25-route-section.network create mode 100644 test/test-network/conf/25-route-static.network delete mode 100644 test/test-network/conf/25-route-tcp-window-settings.network delete mode 100644 test/test-network/conf/25-route-type.network create mode 100644 test/test-network/conf/25-sit-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-sit-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-sysctl-disable-ipv6.network create mode 100644 test/test-network/conf/25-vrf.network create mode 100644 test/test-network/conf/25-vti-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-vti-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-vti6-tunnel-local-any.netdev create mode 100644 test/test-network/conf/25-vti6-tunnel-remote-any.netdev create mode 100644 test/test-network/conf/25-wireguard-preshared-key.txt create mode 100644 test/test-network/conf/25-wireguard-private-key.txt create mode 100644 test/test-network/conf/25-wireguard.netdev.d/peer.conf create mode 100644 test/test-network/conf/25-wireguard.network create mode 100644 test/test-network/conf/bond-slave.network create mode 100644 test/test-network/conf/bond99.network create mode 100644 test/test-network/conf/bridge99-ignore-carrier-loss.network create mode 100644 test/test-network/conf/dhcp-client-gateway-onlink-implicit.network create mode 100644 test/test-network/conf/dhcp-client-vrf.network create mode 100644 test/test-network/conf/erspan.network create mode 100644 test/test-network/conf/ip6gretun.network create mode 100644 test/test-network/conf/routing-policy-rule-dummy98.network rename test/test-network/conf/{routing-policy-rule.network => routing-policy-rule-test1.network} (100%) delete mode 100644 test/test-network/conf/test-static.network create mode 100644 test/test-network/conf/vlan6.netdev create mode 100644 test/test-network/conf/vlan6.network create mode 100644 units/time-set.target create mode 100644 units/usb-gadget.target diff --git a/.ctags b/.ctags new file mode 100644 index 000000000..a75e12e86 --- /dev/null +++ b/.ctags @@ -0,0 +1 @@ +--links=no diff --git a/.dir-locals.el b/.dir-locals.el index 676d75523..815afe172 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -15,7 +15,8 @@ (eval . (c-set-offset 'statement-case-open 0)) (eval . (c-set-offset 'case-label 0)) (eval . (c-set-offset 'arglist-intro '++)) - (eval . (c-set-offset 'arglist-close 0)))) + (eval . (c-set-offset 'arglist-close 0)) + (eval . (c-set-offset 'arglist-cont-nonempty '(c-lineup-gcc-asm-reg c-lineup-arglist))))) (nxml-mode . ((nxml-child-indent . 2) (fill-column . 109))) (meson-mode . ((meson-indent-basic . 8))) diff --git a/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql b/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql new file mode 100644 index 000000000..865330430 --- /dev/null +++ b/.lgtm/cpp-queries/PotentiallyDangerousFunction.ql @@ -0,0 +1,54 @@ +/** + * @name Use of potentially dangerous function + * @description Certain standard library functions are dangerous to call. + * @kind problem + * @problem.severity error + * @precision high + * @id cpp/potentially-dangerous-function + * @tags reliability + * security + * + * Borrowed from + * https://github.com/Semmle/ql/blob/master/cpp/ql/src/Security/CWE/CWE-676/PotentiallyDangerousFunction.ql + */ +import cpp + +predicate potentiallyDangerousFunction(Function f, string message) { + ( + f.getQualifiedName() = "fgets" and + message = "Call to fgets() is potentially dangerous. Use read_line() instead." + ) or ( + f.getQualifiedName() = "strtok" and + message = "Call to strtok() is potentially dangerous. Use extract_first_word() instead." + ) or ( + f.getQualifiedName() = "strsep" and + message = "Call to strsep() is potentially dangerous. Use extract_first_word() instead." + ) or ( + f.getQualifiedName() = "dup" and + message = "Call to dup() is potentially dangerous. Use fcntl(fd, FD_DUPFD_CLOEXEC, 3) instead." + ) or ( + f.getQualifiedName() = "htonl" and + message = "Call to htonl() is confusing. Use htobe32() instead." + ) or ( + f.getQualifiedName() = "htons" and + message = "Call to htons() is confusing. Use htobe16() instead." + ) or ( + f.getQualifiedName() = "ntohl" and + message = "Call to ntohl() is confusing. Use be32toh() instead." + ) or ( + f.getQualifiedName() = "ntohs" and + message = "Call to ntohs() is confusing. Use be16toh() instead." + ) or ( + f.getQualifiedName() = "strerror" and + message = "Call to strerror() is not thread-safe. Use strerror_r() or printf()'s %m format string instead." + ) or ( + f.getQualifiedName() = "accept" and + message = "Call to accept() is not O_CLOEXEC-safe. Use accept4() instead." + ) +} + +from FunctionCall call, Function target, string message +where + call.getTarget() = target and + potentiallyDangerousFunction(target, message) +select call, message diff --git a/.lgtm/cpp-queries/fgets.ql b/.lgtm/cpp-queries/fgets.ql deleted file mode 100644 index a4181e4f3..000000000 --- a/.lgtm/cpp-queries/fgets.ql +++ /dev/null @@ -1,21 +0,0 @@ -/** - * @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 abde31e48..4764ad670 100644 --- a/.mailmap +++ b/.mailmap @@ -198,3 +198,5 @@ David Santamaría Rogado Mike Auty Roger James Stephan Edel +Andrey Yashkin <38919268+AndreyYashkin@users.noreply.github.com> +Ronald Tschalär diff --git a/.mkosi/mkosi.arch b/.mkosi/mkosi.arch index aabfd52ad..0561fece1 100644 --- a/.mkosi/mkosi.arch +++ b/.mkosi/mkosi.arch @@ -13,7 +13,7 @@ Format=raw_btrfs Bootable=yes [Partitions] -RootSize=2G +RootSize=3G [Packages] Cache=/var/cache/pacman/pkg/ diff --git a/NEWS b/NEWS index cd81d9ca3..98d5a1607 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,268 @@ systemd System and Service Manager +CHANGES WITH 242: + + * In .link files, MACAddressPolicy=persistent (the default) is changed + to cover more devices. For devices like bridges, tun, tap, bond, and + similar interfaces that do not have other identifying information, + the interface name is used as the basis for persistent seed for MAC + and IPv4LL addresses. The way that devices that were handled + previously is not changed, and this change is about covering more + devices then previously by the "persistent" policy. + + MACAddressPolicy=random may be used to force randomized MACs and + IPv4LL addresses for a device if desired. + + Hint: the log output from udev (at debug level) was enhanced to + clarify what policy is followed and which attributes are used. + `SYSTEMD_LOG_LEVEL=debug udevadm test-builtin net_setup_link /sys/class/net/` + may be used to view this. + + * The .device units generated by systemd-fstab-generator and other + generators do not automatically pull in the corresponding .mount unit + as a Wants= dependency. This means that simply plugging in the device + will not cause the mount unit to be started automatically. But please + note that the mount unit may be started for other reasons, in + particular if it is part of local-fs.target, and any unit which + (transitively) depends on local-fs.target is started. + + * networkctl list/status/lldp now accept globbing wildcards for network + interface names to match against all existing interfaces. + + * The $PIDFILE environment variable is set to point the absolute path + configured with PIDFile= for processes of that service. + + * The fallback DNS server list was augmented with Cloudflare public DNS + servers. Use `-Ddns-servers=` to set a different fallback. + + * A new special target usb-gadget.target will be started automatically + when a USB Device Controller is detected (which means that the system + is a USB peripheral). + + * A new unit setting CPUQuotaPeriodSec= assigns the time period + relatively to which the CPU time quota specified by CPUQuota= is + measured. + + * A new unit setting ProtectHostname= may be used to prevent services + from modifying hostname information (even if they otherwise would + have privileges to do so). + + * A new unit setting NetworkNamespacePath= may be used to specify a + namespace for service or socket units through a path referring to a + Linux network namespace pseudo-file. + + * The PrivateNetwork= setting and JoinsNamespaceOf= dependencies now + have an effect on .socket units: when used the listening socket is + created within the configured network namespace instead of the host + namespace. + + * ExecStart= command lines in unit files may now be prefixed with ':' + in which case environment variable substitution is + disabled. (Supported for the other ExecXYZ= settings, too.) + + * .timer units gained two new boolean settings OnClockChange= and + OnTimezoneChange= which may be used to also trigger a unit when the + system clock is changed or the local timezone is + modified. systemd-run has been updated to make these options easily + accessible from the command line for transient timers. + + * Two new conditions for units have been added: ConditionMemory= may be + used to conditionalize a unit based on installed system + RAM. ConditionCPUs= may be used to conditionalize a unit based on + install CPU cores. + + * The @default system call filter group understood by SystemCallFilter= + has been updated to include the new rseq() system call introduced in + kernel 4.15. + + * A new time-set.target has been added that indicates that the system + time has been set from a local source (possibly imprecise). The + existing time-sync.target is stronger and indicates that the time has + been synchronized with a precise external source. Services where + approximate time is sufficient should use the new target. + + * "systemctl start" (and related commands) learnt a new + --show-transaction option. If specified brief information about all + jobs queued because of the requested operation is shown. + + * systemd-networkd recognizes a new operation state 'enslaved', used + (instead of 'degraded' or 'carrier') for interfaces which form a + bridge, bond, or similar, and an new 'degraded-carrier' operational + state used for the bond or bridge master interface when one of the + enslaved devices is not operational. + + * .network files learnt the new IgnoreCarrierLoss= option for leaving + networks configured even if the carrier is lost. + + * The RequiredForOnline= setting in .network files may now specify a + minimum operational state required for the interface to be considered + "online" by systemd-networkd-wait-online. Related to this + systemd-networkd-wait-online gained a new option --operational-state= + to configure the same, and its --interface= option was updated to + optionally also take an operational state specific for an interface. + + * systemd-networkd-wait-online gained a new setting --any for waiting + for only one of the requested interfaces instead of all of them. + + * systemd-networkd now implements L2TP tunnels. + + * Two new .network settings UseAutonomousPrefix= and UseOnLinkPrefix= + may be used to cause autonomous and onlink prefixes received in IPv6 + Router Advertisements to be ignored. + + * New MulticastFlood=, NeighborSuppression=, and Learning= .network + file settings may be used to tweak bridge behaviour. + + * The new TripleSampling= option in .network files may be used to + configure CAN triple sampling. + + * A new .netdev settings PrivateKeyFile= and PresharedKeyFile= may be + used to point to private or preshared key for a WireGuard interface. + + * /etc/crypttab now supports the same-cpu-crypt and + submit-from-crypt-cpus options to tweak encryption work scheduling + details. + + * systemd-tmpfiles will now take a BSD file lock before operating on a + contents of directory. This may be used to temporarily exclude + directories from aging by taking the same lock (useful for example + when extracting a tarball into /tmp or /var/tmp as a privileged user, + which might create files with really old timestamps, which + nevertheless should not be deleted). For further details, see: + + https://systemd.io/TEMPORARY_DIRECTORIES + + * systemd-tmpfiles' h line type gained support for the + FS_PROJINHERIT_FL ('P') file attribute (introduced in kernel 4.5), + controlling project quota inheritance. + + * sd-boot and bootctl now implement support for an Extended Boot Loader + (XBOOTLDR) partition, that is intended to be mounted to /boot, in + addition to the ESP partition mounted to /efi or /boot/efi. + Configuration file fragments, kernels, initrds and other EFI images + to boot will be loaded from both the ESP and XBOOTLDR partitions. + The XBOOTLDR partition was previously described by the Boot Loader + Specification, but implementation was missing in sd-boot. Support for + this concept allows using the sd-boot boot loader in more + conservative scenarios where the boot loader itself is placed in the + ESP but the kernels to boot (and their metadata) in a separate + partition. + + * A system may now be booted with systemd.volatile=overlay on the + kernel command line, which causes the root file system to be set up + an overlayfs mount combining the root-only root directory with a + writable tmpfs. In this setup, the underlying root device is not + modified, and any changes are lost at reboot. + + * Similar, systemd-nspawn can now boot containers with a volatile + overlayfs root with the new --volatile=overlay switch. + + * systemd-nspawn can now consume OCI runtime bundles using a new + --oci-bundle= option. This implementation is fully usable, with most + features in the specification implemented, but since this a lot of + new code and functionality, this feature should most likely not + be used in production yet. + + * systemd-nspawn now supports various options described by the OCI + runtime specification on the command-line and in .nspawn files: + --inaccessible=/Inaccessible= may be used to mask parts of the file + system tree, --console=/--pipe may be used to configure how standard + input, output, and error are set up. + + * busctl learned the `emit` verb to generate D-Bus signals. + + * systemd-analyze cat-config may be used to gather and display + configuration spread over multiple files, for example system and user + presets, tmpfiles.d, sysusers.d, udev rules, etc. + + * systemd-analyze calendar now takes an optional new parameter + --iterations= which may be used to show a maximum number of iterations + the specified expression will elapse next. + + * The sd-bus C API gained support for naming method parameters in the + introspection data. + + * systemd-logind gained D-Bus APIs to specify the "reboot parameter" + the reboot() system call expects. + + * journalctl learnt a new --cursor-file= option that points to a file + from which a cursor should be loaded in the beginning and to which + the updated cursor should be stored at the end. + + * ACRN hypervisor and Windows Subsystem for Linux (WSL) are now + detected by systemd-detect-virt (and may also be used in + ConditionVirtualization=). + + * The behaviour of systemd-logind may now be modified with environment + variables $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP, + $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU, and + $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY. They cause logind to either + skip the relevant operation completely (when set to false), or to + create a flag file in /run/systemd (when set to true), instead of + actually commencing the real operation when requested. The presence + of /run/systemd/reboot-to-firmware-setup, + /run/systemd/reboot-to-boot-loader-menu, and + /run/systemd/reboot-to-boot-loader-entry, may be used by alternative + boot loader implementations to replace some steps logind performs + during reboot with their own operations. + + * systemctl can be used to request a reboot into the boot loader menu + or a specific boot loader entry with the new --boot-load-menu= and + --boot-loader-entry= options to a reboot command. (This requires a + boot loader that supports this, for example sd-boot.) + + * kernel-install will no longer unconditionally create the output + directory (e.g. /efi//) for boot loader + snippets, but will do only if the machine-specific parent directory + (i.e. /efi//) already exists. bootctl has been modified + to create this parent directory during sd-boot installation. + + This makes it easier to use kernel-install with plugins which support + a different layout of the bootloader partitions (for example grub2). + + * During package installation (with `ninja install`), we would create + symlinks for systemd-networkd.service, systemd-networkd.socket, + systemd-resolved.service, remote-cryptsetup.target, remote-fs.target, + systemd-networkd-wait-online.service, and systemd-timesyncd.service + in /etc, as if `systemctl enable` was called for those units, to make + the system usable immediately after installation. Now this is not + done anymore, and instead calling `systemctl preset-all` is + recommended after the first installation of systemd. + + * A new boolean sandboxing option RestrictSUIDSGID= has been added that + is built on seccomp. When turned on creation of SUID/SGID files is + prohibited. + + * The NoNewPrivileges= and the new RestrictSUIDSGID= options are now + implied if DynamicUser= is turned on for a service. This hardens + these services, so that they neither can benefit from nor create + SUID/SGID executables. This is a minor compatibility breakage, given + that when DynamicUser= was first introduced SUID/SGID behaviour was + unaffected. However, the security benefit of these two options is + substantial, and the setting is still relatively new, hence we opted + to make it mandatory for services with dynamic users. + + Contributions from: Adam Jackson, Alexander Tsoy, Andrey Yashkin, + Andrzej Pietrasiewicz, Anita Zhang, Balint Reczey, Beniamino Galvani, + Ben Iofel, Benjamin Berg, Benjamin Dahlhoff, Chris, Chris Morin, + Christopher Wong, Claudius Ellsel, Clemens Gruber, dana, Daniel Black, + Davide Cavalca, David Michael, David Rheinsberg, emersion, Evgeny + Vereshchagin, Filipe Brandenburger, Franck Bui, Frantisek Sumsal, + Giacinto Cifelli, Hans de Goede, Hugo Kindel, Ignat Korchagin, Insun + Pyo, Jan Engelhardt, Jonas Dorel, Jonathan Lebon, Jonathon Kowalski, + Jörg Sommer, Jörg Thalheim, Jussi Pakkanen, Kai-Heng Feng, Lennart + Poettering, Lubomir Rintel, Luís Ferreira, Martin Pitt, Matthias + Klumpp, Michael Biebl, Michael Niewöhner, Michael Olbrich, Michal + Sekletar, Mike Lothian, Paul Menzel, Piotr Drąg, Riccardo Schirone, + Robin Elvedi, Roman Kulikov, Ronald Tschalär, Ross Burton, Ryan + Gonzalez, Sebastian Krzyszkowiak, Stephane Chazelas, StKob, Susant + Sahani, Sylvain Plantefève, Szabolcs Fruhwald, Taro Yamada, Theo + Ouzhinski, Thomas Haller, Tobias Jungel, Tom Yan, Tony Asleson, Topi + Miettinen, unixsysadmin, Van Laser, Vesa Jääskeläinen, Yu, Li-Yu, + Yu Watanabe, Zbigniew Jędrzejewski-Szmek + + — Warsaw, 2019-04-11 + CHANGES WITH 241: * The default locale can now be configured at compile time. Otherwise, @@ -83,6 +346,9 @@ CHANGES WITH 241: * udevadm control learnt a new option for --ping for testing whether a systemd-udevd instance is running and reacting. + * udevadm trigger learnt a new option for --wait-daemon for waiting + systemd-udevd daemon to be initialized. + Contributions from: Aaron Plattner, Alberts Muktupāvels, Alex Mayer, Ayman Bagabas, Beniamino Galvani, Burt P, Chris Down, Chris Lamb, Chris Morin, Christian Hesse, Claudius Ellsel, dana, Daniel Axtens, Daniele @@ -101,7 +367,7 @@ CHANGES WITH 241: Miettinen, YiFei Zhu, YmrDtnJu, YunQiang Su, Yu Watanabe, Zbigniew Jędrzejewski-Szmek, zsergeant77, Дамјан Георгиевски - — Berlin, 2018-02-14 + — Berlin, 2019-02-14 CHANGES WITH 240: @@ -1954,12 +2220,14 @@ CHANGES WITH 234: systemd-logind to be safe. See https://cgit.freedesktop.org/xorg/xserver/commit/?id=dc48bd653c7e101.) - * All kernel install plugins are called with the environment variable + * All kernel-install plugins are called with the environment variable KERNEL_INSTALL_MACHINE_ID which is set to the machine ID given by - /etc/machine-id. If the file is missing or empty, the variable is - empty and BOOT_DIR_ABS is the path of a temporary directory which is - removed after all the plugins exit. So, if KERNEL_INSTALL_MACHINE_ID - is empty, all plugins should not put anything in BOOT_DIR_ABS. + /etc/machine-id. If the machine ID could not be determined, + $KERNEL_INSTALL_MACHINE_ID will be empty. Plugins should not put + anything in the entry directory (passed as the second argument) if + $KERNEL_INSTALL_MACHINE_ID is empty. For backwards compatiblity, a + temporary directory is passed as the entry directory and removed + after all the plugins exit. Contributions from: Adrian Heine né Lang, Aggelos Avgerinos, Alexander Kurtz, Alexandros Frantzis, Alexey Brodkin, Alex Lu, Amir Pakdel, Amir @@ -5743,7 +6011,7 @@ CHANGES WITH 214: * Socket units gained a new Symlinks= setting. It takes a list of symlinks to create to file system sockets or FIFOs created by the specific Unix sockets. This is useful to - manage symlinks to socket nodes with the same life-cycle as + manage symlinks to socket nodes with the same lifecycle as the socket itself. * The /dev/log socket and /dev/initctl FIFO have been moved to @@ -6051,7 +6319,7 @@ CHANGES WITH 212: users who are logged out cannot continue to consume IPC resources. This covers SysV memory, semaphores and message queues as well as POSIX shared memory and message - queues. Traditionally, SysV and POSIX IPC had no life-cycle + queues. Traditionally, SysV and POSIX IPC had no lifecycle limits. With this functionality, that is corrected. This may be turned off by using the RemoveIPC= switch of logind.conf. @@ -6201,7 +6469,7 @@ CHANGES WITH 211: systemd-networkd. * The sd-bus.h bus API gained a new sd_bus_track object for - tracking the life-cycle of bus peers. Note that sd-bus.h is + tracking the lifecycle of bus peers. Note that sd-bus.h is still not a public API though (unless you specify --enable-kdbus on the configure command line, which however voids your warranty and you get no API stability guarantee). diff --git a/README b/README index 419f0adb7..c6523193f 100644 --- a/README +++ b/README @@ -158,6 +158,7 @@ REQUIREMENTS: openssl >= 1.1.0 (optional, required to support DNS-over-TLS with openssl) elfutils >= 158 (optional) polkit (optional) + tzdata >= 2014f (optional) pkg-config gperf docbook-xsl (optional, required for documentation) @@ -316,8 +317,7 @@ WARNINGS: that valgrind generates nice output only on exit(), hence on shutdown we don't execve() systemd-shutdown. -STABLE BRANCHES AND BACKPORTS - +STABLE BRANCHES AND BACKPORTS: Stable branches with backported patches are available in the systemd-stable repo at https://github.com/systemd/systemd-stable. diff --git a/README.md b/README.md index 31d530733..72484f620 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Count of open issues over time Count of open pull requests over time [![Semaphore CI Build Status](https://semaphoreci.com/api/v1/projects/28a5a3ca-3c56-4078-8b5e-7ed6ef912e14/443470/shields_badge.svg)](https://semaphoreci.com/systemd/systemd)
+[![Coverity Scan Status](https://scan.coverity.com/projects/350/badge.svg)](https://scan.coverity.com/projects/350)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1369/badge)](https://bestpractices.coreinfrastructure.org/projects/1369)
[![Travis CI Build Status](https://travis-ci.org/systemd/systemd.svg?branch=master)](https://travis-ci.org/systemd/systemd)
[![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)
diff --git a/TODO b/TODO index 462db57a3..663e4fedd 100644 --- a/TODO +++ b/TODO @@ -1,13 +1,9 @@ Bugfixes: -* 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. @@ -23,6 +19,19 @@ Janitorial Clean-ups: Features: +* tweak journald context caching. In addition to caching per-process attributes + keyed by PID, cache per-cgroup attributes (i.e. the various xattrs we read) + keyed by cgroup path, and guarded by ctime changes. This should provide us + with a nice speed-up on services that have many processes running in the same + cgroup. + +* clean up sleep.c: + - Use CLOCK_BOOTTIME_ALARM for waking up s2h instead of RTC ioctls + - Parse sleep.conf only once, and parse its whole contents so that we don't + have to parse it again and again in s2h + - Make sure resume= and resume_offset= on the kernel cmdline always take + precedence + * 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. @@ -51,13 +60,8 @@ Features: * 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 cgroup v2 for all leaf cgroups (kernel v4.19+) * add a new syscall group "@esoteric" for more esoteric stuff such as bpf() and @@ -65,7 +69,7 @@ Features: * 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() + augment them with mlock(). Also use MADV_DONTDUMP * whenever oom_kill memory.event event is triggered print a nice log message @@ -77,8 +81,6 @@ Features: * 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. @@ -90,23 +92,12 @@ Features: 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 @@ -150,9 +141,6 @@ Features: * When reloading configuration PID 1 should reset all its properties to the original defaults before calling parse_config() -* Add OnTimezoneChange= and OnTimeChange= stanzas to .timer units in order to - schedule events based on time and timezone changes. - * nspawn: greater control over selinux label? * hibernate/s2h: make this robust and safe to enable in Fedora by default. @@ -268,8 +256,6 @@ Features: * support projid-based quota in machinectl for containers -* Add NetworkNamespacePath= to specify a path to a network namespace - * maybe use SOURCE_DATE_EPOCH (i.e. the env var the reproducible builds folks introduced) as the RTC epoch, instead of the mtime of NEWS. @@ -449,8 +435,6 @@ Features: * optionally, also require WATCHDOG=1 notifications during service start-up and shutdown -* resolved: when routing queries, make sure only look for the *longest* suffix... - * delay activation of logind until somebody logs in, or when /dev/tty0 pulls it in or lingering is on (so that containers don't bother with it until PAM is used). also exit-on-idle @@ -595,12 +579,6 @@ Features: service instances processing the listening socket, and open this up for ReusePort= -* socket units: support creating sockets in different namespace, - opening it up for JoinsNamespaceOf=. This would require to fork off - a tiny process that joins the namespace and creates/binds the socket - and passes this back to PID1 via SCM_RIGHTS. This also could be used - to allow Chown/chgrp on sockets without requiring NSS in PID 1. - * introduce bus call FreezeUnit(s, b), as well as "systemctl freeze $UNIT" and "systemctl thaw $UNIT" as wrappers around this. The calls should SIGSTOP all unit processes in a loop until all processes of @@ -641,9 +619,6 @@ Features: * load .d/*.conf dropins for device units -* allow implementation of InaccessibleDirectories=/ plus - ReadOnlyDirectories=... for whitelisting files for a service. - * sd-bus: - EBADSLT handling - GetAllProperties() on a non-existing object does not result in a failure currently @@ -710,6 +685,10 @@ Features: - honor language efi variables for default language selection (if there are any?) - honor timezone efi variables for default timezone selection (if there are any?) - change bootctl to be backed by systemd-bootd to control temporary and persistent default boot goal plus efi variables +* bootctl + - verify that the files boot entries point to exist + - recognize the case when not booted on EFI + - specify paths for boot entries * maybe do not install getty@tty1.service symlink in /etc but in /usr? @@ -742,8 +721,6 @@ Features: - follow PropertiesChanged state more closely, to deal with quick logouts and relogins -* exec: when deinitializating a tty device fix the perms and group, too, not only when initializing. Set access mode/gid to 0620/tty. - * journal: - consider introducing implicit _TTY= + _PPID= + _EUID= + _EGID= + _FSUID= + _FSGID= fields - import and delete pstore filesystem content at startup @@ -825,6 +802,7 @@ Features: - man: document the very specific env the shutdown drop-in tools live in - man: add more examples to man pages - man: maybe sort directives in man pages, and take sections from --help and apply them to man too + - document root=gpt-auto properly * systemctl: - add systemctl switch to dump transaction without executing it @@ -844,9 +822,7 @@ Features: * timer units: - timer units should get the ability to trigger when: - o CLOCK_REALTIME makes jumps (TFD_TIMER_CANCEL_ON_SET) o DST changes - o timezone changes - Modulate timer frequency based on battery state * add libsystemd-password or so to query passwords during boot using the password agent logic diff --git a/coccinelle/empty-or-dash.cocci b/coccinelle/empty-or-dash.cocci new file mode 100644 index 000000000..bebaead2f --- /dev/null +++ b/coccinelle/empty-or-dash.cocci @@ -0,0 +1,5 @@ +@@ +expression s; +@@ +- (isempty(s) || streq(s, "-")) ++ empty_or_dash(s) diff --git a/coccinelle/log-json.cocci b/coccinelle/log-json.cocci new file mode 100644 index 000000000..3730fd6ea --- /dev/null +++ b/coccinelle/log-json.cocci @@ -0,0 +1,8 @@ +@@ +expression e, v, flags; +expression list args; +@@ ++ return +- json_log(v, flags, 0, args); ++ json_log(v, flags, SYNTHETIC_ERRNO(e), args); +- return -e; diff --git a/docs/BOOT_LOADER_SPECIFICATION.md b/docs/BOOT_LOADER_SPECIFICATION.md index 3612ff1d3..55a9fef4b 100644 --- a/docs/BOOT_LOADER_SPECIFICATION.md +++ b/docs/BOOT_LOADER_SPECIFICATION.md @@ -4,20 +4,33 @@ title: The Boot Loader Specification # The Boot Loader Specification -_TL;DR: Currently there's little cooperation between multiple distributions in dual-boot (or triple, ... multi-boot) setups, and we'd like to improve this situation by getting everybody to commit to a single boot configuration format that is based on drop-in files, and thus is robust, simple, works without rewriting configuration files and is free of namespace clashes._ +_TL;DR: Currently there's no common boot scheme across architectures and +platforms for open-source operating systems. There's also little cooperation +between multiple distributions in dual-boot (or triple, … multi-boot) +setups. We'd like to improve this situation by getting everybody to commit to a +single boot configuration format that is based on drop-in files, and thus is +robust, simple, works without rewriting configuration files and is free of +namespace clashes._ -The Boot Loader Specification defines a scheme how different operating systems can cooperatively manage a boot loader configuration directory, that accepts drop-in files for boot menu items that are defined in a format that is shared between various boot loader implementations, operating systems, and userspace programs. The target audience for this specification is: +The Boot Loader Specification defines a scheme how different operating systems +can cooperatively manage a boot loader configuration directory, that accepts +drop-in files for boot menu items that are defined in a format that is shared +between various boot loader implementations, operating systems, and userspace +programs. The same scheme can be used to prepare OS media for cases where the +firmware includes a boot loader. The target audience for this specification is: * Boot loader developers, to write a boot loader that directly reads its configuration at runtime from these drop-in snippets +* Firmware developers, to add generic boot loading support directly to the firmware itself * Distribution and Core OS developers, in order to create these snippets at OS/kernel package installation time * UI developers, for implementing a user interface that discovers the available boot options -* OS Installer developers, for setting up the initial drop-in directory +* OS Installer developers, to prepare their installation media and for setting up the initial drop-in directory ## Why is there a need for this specification? Of course, without this specification things already work mostly fine. But here's why we think this specification is needed: * To make the boot more robust, as no explicit rewriting of configuration files is required any more +* To allow an out of the box boot experience on any platform without the need of traditional firmware mechanisms (e.g. BIOS calls, UEFI Boot Services) * 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. * 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. @@ -26,7 +39,9 @@ Of course, without this specification things already work mostly fine. But here' ## Why not simply rely on the EFI boot menu logic? -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: +EFI is not ubiquitous, especially not in embedded systems. If you have an EFI +system, it 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. @@ -42,16 +57,21 @@ Everything described below is located on a placeholder file system `$BOOT`. The * If the OS is installed on a disk with MBR disk label, and a partition with the MBR type id of 0xEA already exists it should be used as `$BOOT`. * Otherwise, if the OS is installed on a disk with MBR disk label, a new partition with MBR type id of 0xEA shall be created, of a suitable size (let's say 500MB), and it should be used as `$BOOT`. * On disks with GPT disk labels - * If the OS is installed on a disk with GPT disk label, and a partition with the GPT type GUID of bc13c2ff-59e6-4262-a352-b275fd6f7172 already exists, it should be used as `$BOOT`. - * Otherwise, if the OS is installed on a disk with GPT disk label, and an ESP partition (i.e. with the GPT type UID of c12a7328-f81f-11d2-ba4b-00a0c93ec93b) already exists and is large enough (let's say 250MB) and otherwise qualifies, it should be used as `$BOOT`. - * Otherwise, if the OS is installed on a disk with GPT disk label, and if the ESP partition already exists but is too small, a new suitably sized (let's say 500MB) partition with GPT type GUID of bc13c2ff-59e6-4262-a352-b275fd6f7172 shall be created and it should be used as `$BOOT`. + * 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 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._ -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. +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 and generic +installation and live media, `$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. 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 @@ -80,7 +100,7 @@ We define two directories below `$BOOT`: 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 one or more spaces 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`. @@ -105,7 +125,16 @@ Each configuration drop-in snippet must include at least a `linux` or an `efi` k linux /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/linux initrd /6a9857a393724b7a981ebb5b8495b9ea/3.8.0-2.fc19.x86_64/initrd -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. +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. + +Conversely, in order to increase compatibility it is recommended to install +generic kernel images that make few assumptions about the firmware they run on, +i.e. it is a good idea that both images shipped as UEFI PE images and those +which are not don't make unnecessary assumption on the underlying firmware, +i.e. don't hard depend on legacy BIOS calls or UEFI boot services. 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). diff --git a/docs/DISTRO_PORTING.md b/docs/DISTRO_PORTING.md index f8b98bcba..620e47e82 100644 --- a/docs/DISTRO_PORTING.md +++ b/docs/DISTRO_PORTING.md @@ -31,6 +31,20 @@ distribution: print the initial transaction it would execute during boot-up. This will also inform you about ordering loops and suchlike. +## Compilation options + +The default configuration does not enable any optimization or hardening +options. This is suitable for development and testing, but not for end-user +installations. + +For deployment, optimization (`-O2` or `-O3` compiler options), link time +optimization (`-Db_lto=true` meson option), and hardening (e.g. +`-D_FORTIFY_SOURCE=2`, `-fstack-protector-strong`, `-fstack-clash-protection`, +`-fcf-protection`, `-pie` compiler options, and `-z relro`, `-z now`, +`--as-needed` linker options) are recommended. The most appropriate set of +options depends on the architecture and distribution specifics so no default is +provided. + ## NTP Pool By default, systemd-timesyncd uses the Google Public NTP servers @@ -49,8 +63,8 @@ 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` +By default, systemd-resolved uses Cloudflare and Google Public DNS servers +`1.1.1.1`, `8.8.8.8`, `1.0.0.1`, `8.8.4.4`, `2606:4700:4700::1111`, `2001:4860:4860::8888`, `2606:4700:4700::1001`, `2001:4860:4860::8844` as fallback, if no other DNS configuration is available. Use `-Ddns-servers=` to direct systemd-resolved to different fallback diff --git a/docs/ENVIRONMENT.md b/docs/ENVIRONMENT.md index 99b5b03b6..f72ed20dd 100644 --- a/docs/ENVIRONMENT.md +++ b/docs/ENVIRONMENT.md @@ -107,6 +107,51 @@ systemd-udevd: with `:` in which case the kernel command line option takes precedence, if it is specified as well. +* `$SYSTEMD_REBOOT_TO_FIRMWARE_SETUP` — if set overrides systemd-logind's + built-in EFI logic of requesting a reboot into the firmware. Takes a + boolean. If set to false the functionality is turned off entirely. If set to + true instead of requesting a reboot into the firmware setup UI through EFI a + file `/run/systemd/reboot-to-firmware-setup` is created whenever this is + requested. This file may be checked for by services run during system + shutdown in order to request the appropriate operation from the firmware in + an alternative fashion. + +* `$SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU` — similar to the above, allows + overriding of systemd-logind's built-in EFI logic of requesting a reboot into + the boot loader menu. Takes a boolean. If set to false the functionality is + turned off entirely. If set to true instead of requesting a reboot into the + boot loader menu through EFI a file `/run/systemd/reboot-to-boot-loader-menu` + is created whenever this is requested. The file contains the requested boot + loader menu timeout in µs, formatted in ASCII decimals, or zero in case no + time-out is requested. This file may be checked for by services run during + system shutdown in order to request the appropriate operation from the boot + loader in an alternative fashion. + +* `$SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY` — similar to the above, allows + overriding of systemd-logind's built-in EFI logic of requesting a reboot into + a specific boot loader entry. Takes a boolean. If set to false the + functionality is turned off entirely. If set to true instead of requesting a + reboot into a specific boot loader entry through EFI a file + `/run/systemd/reboot-to-boot-loader-entry` is created whenever this is + requested. The file contains the requested boot loader entry identifier. This + file may be checked for by services run during system shutdown in order to + request the appropriate operation from the boot loader in an alternative + fashion. Note that by default only boot loader entries which follow the [Boot + Loader Specification](https://systemd.io/BOOT_LOADER_SPECIFICATION) and are + placed in the ESP or the Extended Boot Loader partition may be selected this + way. However, if a directory `/run/boot-loader-entries/` exists, the entries + are loaded from there instead. The directory should contain the usual + directory hierarchy mandated by the Boot Loader Specification, i.e. the entry + drop-ins should be placed in + `/run/boot-loader-entries/loader/entries/*.conf`, and the files referenced by + the drop-ins (including the kernels and initrds) somewhere else below + `/run/boot-loader-entries/`. Note that all these files may be (and are + supposed to be) symlinks. systemd-logind will load these files on-demand, + these files can hence be updated (ideally atomically) whenever the boot + loader configuration changes. A foreign boot loader installer script should + hence synthesize drop-in snippets and symlinks for all boot entries at boot + or whenever they change if it wants to integrate with systemd-logind's APIs. + installed systemd tests: * `$SYSTEMD_TEST_DATA` — override the location of test data. This is useful if diff --git a/docs/PORTABLE_SERVICES.md b/docs/PORTABLE_SERVICES.md index 5b6c085af..194ef5bcd 100644 --- a/docs/PORTABLE_SERVICES.md +++ b/docs/PORTABLE_SERVICES.md @@ -153,9 +153,9 @@ images should just work the way they are. Specifically, the following requirements are made for an image that can be attached/detached with `portablectl`. -1. It must contain a binary (and its dependencies) that shall be invoked, - including all its dependencies. If binary code, the code needs to be - compiled for an architecture compatible with the host. +1. It must contain an executable that shall be invoked, along with all its + dependencies. If binary code, the code needs to be compiled for an + architecture compatible with the host. 2. The image must either be a plain sub-directory (or btrfs subvolume) containing the binaries and its dependencies in a classic Linux OS tree, or @@ -177,15 +177,35 @@ requirements are made for an image that can be attached/detached with 5. The image must contain the files `/etc/resolv.conf` and `/etc/machine-id` (empty files are ok), they will be bind mounted from the host at runtime. -Note that generally images created by tools such as `debootstrap`, `dnf ---installroot=` or `mkosi` qualify for all of the above in one way or -another. If you wonder what the most minimal image would be that complies with -the requirements above, it could consist of this: +6. The image must contain directories `/proc/`, `/sys/`, `/dev/`, `/run/`, + `/tmp/`, `/var/tmp/` that can be mounted over with the corresponding version + from the host. + +7. The OS might require other files or directories to be in place. For example, + if the image is built based on glibc, the dynamic loader needs to be + available in `/lib/ld-linux.so.2` or `/lib64/ld-linux-x86-64.so.2` (or + similar, depending on architecture), and if the distribution implements a + merged `/usr/` tree, this means `/lib` and/or `/lib64` need to be symlinks + to their respective counterparts below `/usr/`. For details see your + distribution's documentation. + +Note that images created by tools such as `debootstrap`, `dnf --installroot=` +or `mkosi` generally qualify for all of the above in one way or another. If you +wonder what the most minimal image would be that complies with the requirements +above, it could consist of this: ``` -/usr/bin/minimald # a statically compiled binary -/usr/lib/systemd/minimal-test.service # the unit file for the service, with ExecStart=/usr/bin/minimald -/usr/lib/os-release # an os-release file explaining what this is +/usr/bin/minimald # a statically compiled binary +/usr/lib/systemd/system/minimal-test.service # the unit file for the service, with ExecStart=/usr/bin/minimald +/usr/lib/os-release # an os-release file explaining what this is +/etc/resolv.conf # empty file to mount over with host's version +/etc/machine-id # ditto +/proc/ # empty directory to use as mount point for host's API fs +/sys/ # ditto +/dev/ # ditto +/run/ # ditto +/tmp/ # ditto +/var/tmp/ # ditto ``` And that's it. @@ -195,6 +215,11 @@ own. If they do, it's fine, it will be ignored by the portable service logic, but they generally don't have to, and it might make sense to avoid any, to keep images minimal. +If the image is writable, and some of the files or directories that are +overmounted from the host do not exist yet they are automatically created. On +read-only, immutable images (e.g. squashfs images) all files and directories to +over-mount must exist already. + Note that as no new image format or metadata is defined, it's very straight-forward to define images than can be made use of it a number of different ways. For example, by using `mkosi -b` you can trivially build a diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 4bf5ab8dc..d842afcd3 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -5,12 +5,15 @@ title: 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") +2. Update the contributors list in NEWS (`ninja -C build git-contrib`) 3. Update the time and place in NEWS -4. Update version in configure.ac and library numbers in Makefile.am -5. Check that "make distcheck" works -6. Tag the release ("make git-tag") -7. Upload the documentation ("make doc-sync") -8. Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones) -9. Send announcement to systemd-devel, with a copy&paste from NEWS -10. Update IRC topic ("/msg chanserv TOPIC #systemd Version NNN released") +4. Update version and library numbers in `meson.build` +5. Tag the release: `version=vXXX-rcY && git tag -s "${version}" -m "systemd ${version}"` +6. Do `ninja -C build` +7. Make sure that the version string and package string match: `build/systemctl --version` +8. Upload the documentation: `ninja -C build doc-sync` +9. [After final release] Close the github milestone and open a new one (https://github.com/systemd/systemd/milestones) +10. "Draft" a new release on github (https://github.com/systemd/systemd/releases/new), mark "This is a pre-release" if appropriate. +11. Check that announcement to systemd-devel, with a copy&paste from NEWS, was sent. This should happen automatically. +12. Update IRC topic (`/msg chanserv TOPIC #systemd Version NNN released`) +13. [After final release] Also push commits to stable, create an empty -stable branch: `git push systemd-stable origin/master:master origin/master:v${version}-stable`, and change the default branch to latest release (https://github.com/systemd/systemd-stable/settings/branches). diff --git a/docs/TEMPORARY_DIRECTORIES.md b/docs/TEMPORARY_DIRECTORIES.md new file mode 100644 index 000000000..0ea88f8fc --- /dev/null +++ b/docs/TEMPORARY_DIRECTORIES.md @@ -0,0 +1,220 @@ +--- +title: Using /tmp/ And /var/tmp/ Safely +--- + +# Using `/tmp/` And `/var/tmp/` Safely + +`/tmp/` and `/var/tmp/` are two world-writable directories Linux systems +provide for temporary files. The former is typically on `tmpfs` and thus +backed by RAM/swap, and flushed out on each reboot. The latter is typically a +proper, persistent file system, and thus backed by physical storage. This +means: + +1. `/tmp/` should be used for smaller, size-bounded files only; `/var/tmp/` + should be used for everything else. + +2. Data that shall survive a boot cycle shouldn't be placed in `/tmp/`. + +If the `$TMPDIR` environment variable is set, use that path, and neither use +`/tmp/` nor `/var/tmp/` directly. + +See +[file-hierarchy(7)](https://www.freedesktop.org/software/systemd/man/file-hierarchy.html) +for details about these two (and most other) directories of a Linux system. + +## Common Namespace + +Note that `/tmp/` and `/var/tmp/` each define a common namespace shared by all +local software. This means guessable file or directory names below either +directory directly translate into a 🚨 Denial-of-Service (DoS) 🚨 vulnerability +or worse: if some software creates a file or directory `/tmp/foo` then any +other software that wants to create the same file or directory `/tmp/foo` +either will fail (as the file already exists) or might be tricked into using +untrusted files. Hence: do not use guessable names in `/tmp/` or `/var/tmp/` — +if you do you open yourself up to a local DoS exploit or worse. (You can get +away with using guessable names, if you pre-create subdirectories below `/tmp/` +for them, like X11 does with `/tmp/.X11-unix/` through `tmpfiles.d/` +drop-ins. However this is not recommended, as it is fully safe only if these +directories are pre-created during early boot, and thus problematic if package +installation during runtime is permitted.) + +To protect yourself against these kinds of attacks Linux provides a couple of +APIs that help you avoiding guessable names. Specifically: + +1. Use [`mkstemp()`](http://man7.org/linux/man-pages/man3/mkstemp.3.html) + (POSIX), `mkostemp()` (glibc), + [`mkdtemp()`](http://man7.org/linux/man-pages/man3/mkdtemp.3.html) (POSIX), + [`tmpfile()`](http://man7.org/linux/man-pages/man3/tmpfile.3.html) (C89) + +2. Use [`open()`](http://man7.org/linux/man-pages/man2/open.2.html) with + `O_TMPFILE` (Linux) + +3. [`memfd_create()`](http://man7.org/linux/man-pages/man2/memfd_create.2.html) + (Linux; this doesn't bother with `/tmp/` or `/var/tmp/` at all, but uses the + same RAM/swap backing as `tmpfs` uses, hence is very similar to `/tmp/` + semantics.) + +For system services systemd provides the `PrivateTmp=` boolean setting. If +turned on for a service (👍 which is highly recommended), `/tmp/` and +`/var/tmp/` are replaced by private sub-directories, implemented through Linux +file system namespacing and bind mounts. This means from the service's point of +view `/tmp/` and `/var/tmp/` look and behave like they normally do, but in +reality they are private sub-directories of the host's real `/tmp/` and +`/var/tmp/`, and thus not system-wide locations anymore, but service-specific +ones. This reduces the surface for local DoS attacks substantially. While it is +recommended to turn this option on, it's highly recommended for applications +not to rely on this solely to avoid DoS vulnerabilities, because this option is +not available in environments where file system namespaces are prohibited, for +example in certain container environments. This option is hence an extra line +of defense, but should not be used as an excuse to rely on guessable names in +`/tmp/` and `/var/tmp/`. When this option is used, the per-service temporary +directories are removed whenever the service shuts down, hence the lifecycle of +temporary files stored in it is substantially different from the case where +this option is not used. Also note that some applications use `/tmp/` and +`/var/tmp/` for sharing files and directories. If this option is turned on this +is not possible anymore as after all each service gets its own instances of +both directories. + +## Automatic Clean-Up + +By default, `systemd-tmpfiles` will apply a concept of ⚠️ "ageing" to all files +and directories stored in `/tmp/` and `/var/tmp/`. This means that files that +have neither been changed nor read within a specific time frame are +automatically removed in regular intervals. (This concept is not new to +`systemd-tmpfiles` btw, it's inherited from previous subsystems such as +`tmpwatch`.) By default files in `/tmp/` are cleaned up after 10 days, and +those in `/var/tmp` after 30 days. + +This automatic clean-up is important to ensure disk usage of these temporary +directories doesn't grow without bounds, even when programs abort unexpectedly +or otherwise don't clean up the temporary files/directories they create. On the +other hand it creates problems for long-running software that does not expect +temporary files it operates on to be suddenly removed. There are a couple of +strategies to avoid these issues: + +1. Make sure to always keep a file descriptor to the temporary files you + operate on open, and only access the files through them. This way it doesn't + matter whether the files have been unlinked from the file system: as long as + you have the file descriptor open you can still access the file for both + reading and writing. When operating this way it is recommended to delete the + files right after creating them to ensure that on unexpected program + termination the files or directories are implicitly released by the kernel. + +2. 🥇 Use `memfd_create()` or `O_TMPFILE`. This is an extension of the + suggestion above: files created this way are never linked under a filename + in the file system. This means they are not subject to ageing (as they come + unlinked out of the box), and there's no time window where a directory entry + for the file exists in the file system, and thus behaviour is fully robust + towards unexpected program termination as there are never files on disk that + need to be explicitly deleted. + +3. 🥇 Operate below a sub-directory of `/tmp/` and `/var/tmp/` you created, and + take a BSD file lock ([`flock(dir_fd, + LOCK_SH)`](http://man7.org/linux/man-pages/man2/flock.2.html)) on that + sub-directory. This is particularly interesting when operating on more than + a single file, or on file nodes that are not plain regular files, for + example when extracting a tarball to a temporary directory. The ageing + algorithm will skip all directories (and everything below them) that are + locked through a BSD file lock. As BSD file locks are automatically released + when the file descriptor they are taken on is closed, and all file + descriptors opened by a process are implicitly closed when it exits, this is + a robust mechanism that ensures all temporary files are subject to ageing + when the program that owns them dies, but not while it is still running. Use + this when decompressing tarballs that contain files with old + modification/access times, as extracted files are otherwise immediately + candidates for deletion by the ageing algorithm. The + [`flock`](http://man7.org/linux/man-pages/man1/flock.1.html) tool of the + `util-linux` packages makes this concept available to shell scripts. Note + that `systemd-tmpfiles` only checks for BSD file locks on directories, locks + on other types of file nodes (including regular files) are not considered. + +4. Keep the access time of all temporary files created current. In regular + intervals, use `utimensat()` or a related call to update the access time + ("atime") of all files that shall be kept around. Since the ageing algorithm + looks at the access time of files when deciding whether to delete them, it's + sufficient to update their access times in sufficiently frequent intervals to + ensure the files are not deleted. Since most applications (and tools such as + `ls`) primarily care for the modification time (rather than the access time) + using the access time for this purpose should be acceptable. + +5. Set the "sticky" bit on regular files. The ageing logic skips deletion of + all regular files that have the sticky bit (`chmod +t`) set. This is + honoured for regular files only however, and has no effect on directories as + the sticky bit has a different meaning for them. + +6. Don't use `/tmp/` or `/var/tmp/`, but use your own sub-directory under + `/run/` or `$XDG_RUNTIME_DIRECTORY` (the former if privileged, the latter if + unprivileged), or `/var/lib/` and `~/.config/` (similar, but with + persistency and suitable for larger data). The two temporary directories + `/tmp/` and `/var/tmp/` come with the implicit clean-up semantics described + above. When this is not desired, it's possible to create private per-package + runtime or state directories, and place all temporary files there. However, + do note that this means opting out of any kind of automatic clean-up, and it + is hence particularly essential that the program cleans up generated files + in these directories when they are no longer needed, in particular when the + program dies unexpectedly. Note: this strategy is only really suitable for + packages that operate in a "system wide singleton" fashion with "long" + persistance of its data or state, i.e. as opposed to programs that run in + multiple parallel or short-living instances. This is because a private + directory under `/run` (and the other mentioned directories) is itself + system and package specific singleton with greater longevity. + +5. Exclude your temporary files from clean-ups via a `tmpfiles.d/` drop-in + (which includes drop-ins in the runtime-only directory + `/run/tmpfiles.d/`). The `x`/`X` line types may be used to exclude files + matching the specified globbing patterns from the ageing logic. If this is + used, automatic clean-up is not done for matching files and directory, and + much like with the previous option it's hence essential that the program + generating these temporary files carefully removes the temporary files it + creates again, and in particular so if it dies unexpectedly. + +🥇 The semantics of options 2 (in case you only deal with temporary files, not +directories) and 3 (in case you deal with both) in the list above are in most +cases the most preferable. It is thus recommended to stick to these two +options. + +While the ageing logic is very useful as a safety concept to ensure unused +files and directories are eventually removed a well written program avoids even +creating files that need such a clean-up. In particular: + +1. Use `memfd_create()` or `O_TMPFILE` when creating temporary files. + +2. `unlink()` temporary files right after creating them. This is very similar + to `O_TMPFILE` behaviour: consider deleting temporary files right after + creating them, while keeping open a file descriptor to them. Unlike + `O_TMPFILE` this method also works on older Linux systems and other OSes + that do not implement `O_TMPFILE`. + +## Disk Quota + +Generally, files allocated from `/tmp/` and `/var/tmp/` are allocated from a +pool shared by all local users. Moreover the space available in `/tmp/` is +generally more restricted than `/var/tmp/`. This means, that in particular in +`/tmp/` space should be considered scarce, and programs need to be prepared +that no space is available. Essential programs might require a fallback logic +using a different location for storing temporary files hence. Non-essential +programs at least need to be prepared for `ENOSPC` errors and generate useful, +actionable error messages. + +Some setups employ per-user quota on `/var/tmp/` and possibly `/tmp/`, to make +`ENOSPC` situations less likely, and harder to trigger from unprivileged +users. However, in the general case no such per-user quota is implemented +though, in particular not when `tmpfs` is used as backing file system, because +— even today — `tmpfs` still provides no native quota support in the kernel. + +## Early Boot Considerations + +Both `/tmp/` and `/var/tmp/` are not necessarily available during early boot, +or — if they are available early — are not writable. This means software that +is intended to run during early boot (i.e. before `basic.target` — or more +specifically `local-fs.target` — is up) should not attempt to make use of +either. Interfaces such as `memfd_create()` or files below a package-specific +directory in `/run/` are much better options in this case. (Note that some +packages instead use `/dev/shm/` for temporary files during early boot; this is +not advisable however, as it offers no benefits over a private directory in +`/run/` as both are backed by the same concept: `tmpfs`. The directory +`/dev/shm/` exists to back POSIX shared memory (see +[`shm_open()`](http://man7.org/linux/man-pages/man3/shm_open.3.html) and +related calls), and not as a place for temporary files. `/dev/shm` is +problematic as it is world-writable and there's no automatic clean-up logic in +place.) diff --git a/docs/TRANSIENT-SETTINGS.md b/docs/TRANSIENT-SETTINGS.md index 0ac77f049..f081fdb2c 100644 --- a/docs/TRANSIENT-SETTINGS.md +++ b/docs/TRANSIENT-SETTINGS.md @@ -155,6 +155,7 @@ All execution-related settings are available for transient units. ✓ MemoryDenyWriteExecute= ✓ RestrictNamespaces= ✓ RestrictRealtime= +✓ RestrictSUIDSGID= ✓ RestrictAddressFamilies= ✓ LockPersonality= ✓ LimitCPU= @@ -224,6 +225,7 @@ All cgroup/resource control settings are available for transient units ✓ CPUShares= ✓ StartupCPUShares= ✓ CPUQuota= +✓ CPUQuotaPeriodSec= ✓ MemoryAccounting= ✓ MemoryMin= ✓ MemoryLow= @@ -334,10 +336,12 @@ All automount unit setting is available to transient units: Most timer unit settings are available to transient units. ``` -✓ OnCalendar= ✓ OnActiveSec= ✓ OnBootSec= +✓ OnCalendar= +✓ OnClockChange= ✓ OnStartupSec= +✓ OnTimezoneChange ✓ OnUnitActiveSec= ✓ OnUnitInactiveSec= ✓ Persistent= diff --git a/hwdb/20-OUI.hwdb b/hwdb/20-OUI.hwdb index d65cd6dc4..769404a6e 100644 --- a/hwdb/20-OUI.hwdb +++ b/hwdb/20-OUI.hwdb @@ -3666,7 +3666,7 @@ OUI:0004C3* ID_OUI_FROM_DATABASE=CASTOR Informatique OUI:0004C4* - ID_OUI_FROM_DATABASE=Allen & Heath Limited + ID_OUI_FROM_DATABASE=Audiotonix Group Limited OUI:0004C5* ID_OUI_FROM_DATABASE=ASE Technologies, USA @@ -6348,7 +6348,7 @@ OUI:00085C* ID_OUI_FROM_DATABASE=Shanghai Dare Technologies Co. Ltd. OUI:00085D* - ID_OUI_FROM_DATABASE=Aastra + ID_OUI_FROM_DATABASE=Mitel Corporation OUI:00085E* ID_OUI_FROM_DATABASE=PCO AG @@ -9462,7 +9462,7 @@ OUI:000C6B* ID_OUI_FROM_DATABASE=Kurz Industrie-Elektronik GmbH OUI:000C6C* - ID_OUI_FROM_DATABASE=Elgato Systems LLC + ID_OUI_FROM_DATABASE=Eve Systems GmbH OUI:000C6D* ID_OUI_FROM_DATABASE=Edwards Ltd. @@ -9903,7 +9903,7 @@ OUI:000CFE* ID_OUI_FROM_DATABASE=Grand Electronic Co., Ltd OUI:000CFF* - ID_OUI_FROM_DATABASE=MRO-TEK LIMITED + ID_OUI_FROM_DATABASE=MRO-TEK Realty Limited OUI:000D00* ID_OUI_FROM_DATABASE=Seaway Networks Inc. @@ -24579,7 +24579,7 @@ OUI:001F52* ID_OUI_FROM_DATABASE=UVT Unternehmensberatung fur Verkehr und Technik GmbH OUI:001F53* - ID_OUI_FROM_DATABASE=GEMAC Gesellschaft für Mikroelektronikanwendung Chemnitz mbH + ID_OUI_FROM_DATABASE=GEMAC Chemnitz GmbH OUI:001F54* ID_OUI_FROM_DATABASE=Lorex Technology Inc. @@ -29301,7 +29301,7 @@ OUI:00257D* ID_OUI_FROM_DATABASE=PointRed Telecom Private Ltd. OUI:00257E* - ID_OUI_FROM_DATABASE=NEW POS Technology Limited + ID_OUI_FROM_DATABASE=NEW POS TECHNOLOGY LIMITED OUI:00257F* ID_OUI_FROM_DATABASE=CallTechSolution Co.,Ltd @@ -31397,6 +31397,9 @@ OUI:003532* OUI:003560* ID_OUI_FROM_DATABASE=Rosen Aviation +OUI:0035FF* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:003676* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -31698,7 +31701,7 @@ OUI:004050* ID_OUI_FROM_DATABASE=IRONICS, INCORPORATED OUI:004051* - ID_OUI_FROM_DATABASE=GRACILIS, INC. + ID_OUI_FROM_DATABASE=Garbee and Garbee OUI:004052* ID_OUI_FROM_DATABASE=STAR TECHNOLOGIES, INC. @@ -33968,6 +33971,9 @@ OUI:0076B1* OUI:00778D* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:0077E4* + ID_OUI_FROM_DATABASE=Nokia + OUI:007888* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -34416,7 +34422,7 @@ OUI:00808B* ID_OUI_FROM_DATABASE=DACOLL LIMITED OUI:00808C* - ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + ID_OUI_FROM_DATABASE=NetAlly OUI:00808D* ID_OUI_FROM_DATABASE=WESTCOAST TECHNOLOGY B.V. @@ -35673,7 +35679,7 @@ OUI:00A00D* ID_OUI_FROM_DATABASE=THE PANDA PROJECT OUI:00A00E* - ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + ID_OUI_FROM_DATABASE=NetAlly OUI:00A00F* ID_OUI_FROM_DATABASE=Broadband Technologies @@ -36461,6 +36467,9 @@ OUI:00ACE0* OUI:00AD24* ID_OUI_FROM_DATABASE=D-Link International +OUI:00AD63* + ID_OUI_FROM_DATABASE=Dedicated Micros Malta LTD + OUI:00AECD* ID_OUI_FROM_DATABASE=Pensando Systems @@ -36750,7 +36759,7 @@ OUI:00C016* ID_OUI_FROM_DATABASE=ELECTRONIC THEATRE CONTROLS OUI:00C017* - ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + ID_OUI_FROM_DATABASE=NetAlly OUI:00C018* ID_OUI_FROM_DATABASE=LANART CORPORATION @@ -39170,6 +39179,9 @@ OUI:00EBD5* OUI:00EC0A* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd +OUI:00EDB8* + ID_OUI_FROM_DATABASE=KYOCERA Corporation + OUI:00EEAB* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -39200,6 +39212,9 @@ OUI:00F48D* OUI:00F4B9* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:00F620* + ID_OUI_FROM_DATABASE=Google, Inc. + OUI:00F663* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -39218,6 +39233,9 @@ OUI:00F860* OUI:00F871* ID_OUI_FROM_DATABASE=DGS Denmark A/S +OUI:00FA21* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:00FA3B* ID_OUI_FROM_DATABASE=CLOOS ELECTRONIC GMBH @@ -39497,6 +39515,9 @@ OUI:045A95* OUI:045C06* ID_OUI_FROM_DATABASE=Zmodo Technology Corporation +OUI:045C6C* + ID_OUI_FROM_DATABASE=Juniper Networks + OUI:045C8E* ID_OUI_FROM_DATABASE=gosund GROUP CO.,LTD @@ -39632,6 +39653,9 @@ OUI:0481AE* OUI:04848A* ID_OUI_FROM_DATABASE=7INOVA TECHNOLOGY LIMITED +OUI:04885F* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:04888C* ID_OUI_FROM_DATABASE=Eifelwerk Butler Systeme GmbH @@ -39696,7 +39720,7 @@ OUI:049F06* ID_OUI_FROM_DATABASE=Smobile Co., Ltd. OUI:049F81* - ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + ID_OUI_FROM_DATABASE=NetAlly OUI:049FCA* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -39866,6 +39890,9 @@ OUI:04D437* OUI:04D4C4* ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. +OUI:04D590* + ID_OUI_FROM_DATABASE=Fortinet, Inc. + OUI:04D6AA* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) @@ -39875,6 +39902,9 @@ OUI:04D783* OUI:04D7A5* ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd +OUI:04D9F5* + ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. + OUI:04DAD2* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -40556,6 +40586,9 @@ OUI:08379C* OUI:0838A5* ID_OUI_FROM_DATABASE=Funkwerk plettac electronic GmbH +OUI:083A2F* + ID_OUI_FROM_DATABASE=Guangzhou Juan Intelligent Tech Joint Stock Co.,Ltd + OUI:083A5C* ID_OUI_FROM_DATABASE=Junilab, Inc. @@ -40593,7 +40626,7 @@ OUI:084656* ID_OUI_FROM_DATABASE=VEO-LABS OUI:0847D0* - ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co. Ltd.) + ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co., Ltd. OUI:08482C* ID_OUI_FROM_DATABASE=Raycore Taiwan Co., LTD. @@ -40607,6 +40640,9 @@ OUI:084E1C* OUI:084EBF* ID_OUI_FROM_DATABASE=Broad Net Mux Corporation +OUI:084F0A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:085114* ID_OUI_FROM_DATABASE=QINGDAO TOPSCOMM COMMUNICATION CO., LTD @@ -40706,6 +40742,9 @@ OUI:087CBE* OUI:087D21* ID_OUI_FROM_DATABASE=Altasec technology corporation +OUI:087E64* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + OUI:087F98* ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. @@ -40767,7 +40806,7 @@ OUI:089B4B* ID_OUI_FROM_DATABASE=iKuai Networks OUI:089C86* - ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co. Ltd.) + ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co., Ltd. OUI:089E01* ID_OUI_FROM_DATABASE=QUANTA COMPUTER INC. @@ -40979,6 +41018,9 @@ OUI:08ED02E* OUI:08EDB9* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:08EDED* + ID_OUI_FROM_DATABASE=Zhejiang Dahua Technology Co., Ltd. + OUI:08EE8B* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -41495,6 +41537,9 @@ OUI:0CB5DE* OUI:0CB6D2* ID_OUI_FROM_DATABASE=D-Link International +OUI:0CB771* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:0CB912* ID_OUI_FROM_DATABASE=JM-DATA GmbH @@ -42683,6 +42728,9 @@ OUI:144146* OUI:1441E2* ID_OUI_FROM_DATABASE=Monaco Enterprises, Inc. +OUI:1442FC* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:144319* ID_OUI_FROM_DATABASE=Creative&Link Technology Limited @@ -42701,6 +42749,9 @@ OUI:14488B* OUI:144978* ID_OUI_FROM_DATABASE=Digital Control Incorporated +OUI:1449BC* + ID_OUI_FROM_DATABASE=DrayTek Corp. + OUI:1449E0* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) @@ -42857,6 +42908,9 @@ OUI:147DC5* OUI:14825B* ID_OUI_FROM_DATABASE=Hefei Radio Communication Technology Co., Ltd +OUI:148430* + ID_OUI_FROM_DATABASE=MITAC COMPUTING TECHNOLOGY CORPORATION + OUI:148692* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -42929,6 +42983,9 @@ OUI:149FE8* OUI:14A0F8* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:14A2A0* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:14A364* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -42956,6 +43013,9 @@ OUI:14ABC5* OUI:14ABF0* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. +OUI:14ADCA* + ID_OUI_FROM_DATABASE=China Mobile Iot Limited company + OUI:14AEDB* ID_OUI_FROM_DATABASE=VTech Telecommunications Ltd. @@ -42995,6 +43055,9 @@ OUI:14BB6E* OUI:14BD61* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:14C03E* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:14C089* ID_OUI_FROM_DATABASE=DUNE HD LTD @@ -43190,6 +43253,9 @@ OUI:181725* OUI:18193F* ID_OUI_FROM_DATABASE=Tamtron Oy +OUI:1819D6* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:181BEB* ID_OUI_FROM_DATABASE=Actiontec Electronics, Inc @@ -43628,6 +43694,9 @@ OUI:18A99B* OUI:18AA45* ID_OUI_FROM_DATABASE=Fon Technology +OUI:18AACA* + ID_OUI_FROM_DATABASE=Sichuan tianyi kanghe communications co., LTD + OUI:18ABF5* ID_OUI_FROM_DATABASE=Ultra Electronics Electrics @@ -43664,6 +43733,9 @@ OUI:18B430* OUI:18B591* ID_OUI_FROM_DATABASE=I-Storm +OUI:18B6F7* + ID_OUI_FROM_DATABASE=NEW POS TECHNOLOGY LIMITED + OUI:18B79E* ID_OUI_FROM_DATABASE=Invoxia @@ -43712,6 +43784,9 @@ OUI:18CC23* OUI:18CC88* ID_OUI_FROM_DATABASE=Hitachi Johnson Controls Air +OUI:18CF24* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:18CF5E* ID_OUI_FROM_DATABASE=Liteon Technology Corporation @@ -43913,6 +43988,9 @@ OUI:1C1EE3* OUI:1C1FD4* ID_OUI_FROM_DATABASE=LifeBEAM Technologies LTD +OUI:1C20DB* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:1C21D10* ID_OUI_FROM_DATABASE=Toyo System CO.,LTD. @@ -44012,6 +44090,9 @@ OUI:1C398A* OUI:1C3A4F* ID_OUI_FROM_DATABASE=AccuSpec Electronics, LLC +OUI:1C3A60* + ID_OUI_FROM_DATABASE=Ruckus Wireless + OUI:1C3ADE* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -44126,6 +44207,9 @@ OUI:1C62B8* OUI:1C63B7* ID_OUI_FROM_DATABASE=OpenProducts 237 AB +OUI:1C6499* + ID_OUI_FROM_DATABASE=Comtrend Corporation + OUI:1C659D* ID_OUI_FROM_DATABASE=Liteon Technology Corporation @@ -44210,6 +44294,51 @@ OUI:1C7EE5* OUI:1C7F2C* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:1C82590* + ID_OUI_FROM_DATABASE=Shandong Luneng Intelligence Technology CO., Ltd + +OUI:1C82591* + ID_OUI_FROM_DATABASE=3xLOGIC Inc. + +OUI:1C82592* + ID_OUI_FROM_DATABASE=Diatrend Corporation + +OUI:1C82593* + ID_OUI_FROM_DATABASE=C&A Marketing, INC. + +OUI:1C82594* + ID_OUI_FROM_DATABASE=winsun AG + +OUI:1C82595* + ID_OUI_FROM_DATABASE=Fagus-GreCon Greten GmbH & Co. KG + +OUI:1C82596* + ID_OUI_FROM_DATABASE=CGI IT UK LIMITED + +OUI:1C82597* + ID_OUI_FROM_DATABASE=Jump Trading + +OUI:1C82598* + ID_OUI_FROM_DATABASE=SHENZHEN AOA TECHNOLOGY CO.,LTD + +OUI:1C82599* + ID_OUI_FROM_DATABASE=Shanghai Xiaoyan Technology Co., Ltd. + +OUI:1C8259A* + ID_OUI_FROM_DATABASE=ESTec Corporation + +OUI:1C8259B* + ID_OUI_FROM_DATABASE=KeyWest Networks, Inc + +OUI:1C8259C* + ID_OUI_FROM_DATABASE=Evondos Oy + +OUI:1C8259D* + ID_OUI_FROM_DATABASE=Applied Concepts, Inc. + +OUI:1C8259E* + ID_OUI_FROM_DATABASE=Microtronics Engineering GmbH + OUI:1C8341* ID_OUI_FROM_DATABASE=Hefei Bitland Information Technology Co.Ltd @@ -44552,6 +44681,9 @@ OUI:1CB3E9* OUI:1CB72C* ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. +OUI:1CB796* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:1CB857* ID_OUI_FROM_DATABASE=Becon Technologies Co,.Ltd. @@ -44876,6 +45008,9 @@ OUI:2016B9* OUI:2016D8* ID_OUI_FROM_DATABASE=Liteon Technology Corporation +OUI:201742* + ID_OUI_FROM_DATABASE=LG Electronics + OUI:20180E* ID_OUI_FROM_DATABASE=Shenzhen Sunchip Technology Co., Ltd @@ -45386,6 +45521,9 @@ OUI:20F41B* OUI:20F452* ID_OUI_FROM_DATABASE=Shanghai IUV Software Development Co. Ltd +OUI:20F478* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:20F510* ID_OUI_FROM_DATABASE=Codex Digital Limited @@ -45473,6 +45611,9 @@ OUI:241148* OUI:2411D0* ID_OUI_FROM_DATABASE=Chongqing Ehs Science and Technology Development Co.,Ltd. +OUI:24166D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:24181D* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO-MECHANICS(THAILAND) @@ -45665,6 +45806,9 @@ OUI:24615A* OUI:246278* ID_OUI_FROM_DATABASE=sysmocom - systems for mobile communications GmbH +OUI:2462AB* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:2464EF* ID_OUI_FROM_DATABASE=CYG SUNRI CO.,LTD. @@ -45992,6 +46136,9 @@ OUI:24ECD6* OUI:24EE3A* ID_OUI_FROM_DATABASE=Chengdu Yingji Electronic Hi-tech Co Ltd +OUI:24EE9A* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:24F094* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -46292,6 +46439,9 @@ OUI:28401A* OUI:284121* ID_OUI_FROM_DATABASE=OptiSense Network, LLC +OUI:2841C6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:284430* ID_OUI_FROM_DATABASE=GenesisTechnical Systems (UK) Ltd @@ -46460,6 +46610,9 @@ OUI:28987B* OUI:28993A* ID_OUI_FROM_DATABASE=Arista Networks +OUI:2899C7* + ID_OUI_FROM_DATABASE=LINDSAY BROADBAND INC + OUI:289A4B* ID_OUI_FROM_DATABASE=SteelSeries ApS @@ -46631,6 +46784,9 @@ OUI:28D0CB* OUI:28D1AF* ID_OUI_FROM_DATABASE=Nokia Corporation +OUI:28D1B7* + ID_OUI_FROM_DATABASE=Shenzhen YOUHUA Technology Co., Ltd + OUI:28D244* ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology Co., Ltd. @@ -46916,6 +47072,9 @@ OUI:2C1CF6* OUI:2C1DB8* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. +OUI:2C1E4F* + ID_OUI_FROM_DATABASE=Chengdu Qianli Network Technology Co., Ltd. + OUI:2C1EEA* ID_OUI_FROM_DATABASE=AERODEV @@ -47201,6 +47360,9 @@ OUI:2C4D54* OUI:2C4D79* ID_OUI_FROM_DATABASE=WEIFANG GOERTEK ELECTRONICS CO.,LTD +OUI:2C4F52* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:2C5089* ID_OUI_FROM_DATABASE=Shenzhen Kaixuan Visual Technology Co.,Limited @@ -47702,6 +47864,9 @@ OUI:2CFDAB* OUI:2CFF65* ID_OUI_FROM_DATABASE=Oki Electric Industry Co., Ltd. +OUI:2CFFEE* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:30053F* ID_OUI_FROM_DATABASE=JTI Co.,Ltd. @@ -47921,6 +48086,9 @@ OUI:3029BE* OUI:302DE8* ID_OUI_FROM_DATABASE=JDA, LLC (JDA Systems) +OUI:30317D* + ID_OUI_FROM_DATABASE=Hosiden Corporation + OUI:303294* ID_OUI_FROM_DATABASE=W-IE-NE-R Plein & Baus GmbH @@ -49274,6 +49442,9 @@ OUI:34FCEF* OUI:380025* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:380118* + ID_OUI_FROM_DATABASE=ULVAC,Inc. + OUI:380195* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -49349,6 +49520,9 @@ OUI:3817E1* OUI:38192F* ID_OUI_FROM_DATABASE=Nokia Corporation +OUI:381A52* + ID_OUI_FROM_DATABASE=Seiko Epson Corporation + OUI:381C1A* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -49466,6 +49640,9 @@ OUI:383A21D* OUI:383A21E* ID_OUI_FROM_DATABASE=SDNware technology co.,LTD +OUI:383B26* + ID_OUI_FROM_DATABASE=Jiangsu Qinheng Co., Ltd. + OUI:383BC8* ID_OUI_FROM_DATABASE=2Wire Inc @@ -49496,6 +49673,9 @@ OUI:38458C* OUI:384608* ID_OUI_FROM_DATABASE=zte corporation +OUI:3847BC* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:38484C* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -49697,6 +49877,9 @@ OUI:389496* OUI:3894E0* ID_OUI_FROM_DATABASE=Syrotech Networks. Ltd. +OUI:3894ED* + ID_OUI_FROM_DATABASE=NETGEAR + OUI:389592* ID_OUI_FROM_DATABASE=Beijing Tendyron Corporation @@ -50003,6 +50186,9 @@ OUI:38ED18* OUI:38EE9D* ID_OUI_FROM_DATABASE=Anedo Ltd. +OUI:38EFE3* + ID_OUI_FROM_DATABASE=INGENICO TERMINALS SAS + OUI:38F098* ID_OUI_FROM_DATABASE=Vapor Stone Rail Systems @@ -50750,6 +50936,9 @@ OUI:3CB6B7* OUI:3CB72B* ID_OUI_FROM_DATABASE=PLUMgrid Inc +OUI:3CB74B* + ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. + OUI:3CB792* ID_OUI_FROM_DATABASE=Hitachi Maxell, Ltd., Optronics Division @@ -51017,6 +51206,9 @@ OUI:40270B* OUI:402814* ID_OUI_FROM_DATABASE=RFI Engineering +OUI:402B50* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:402BA1* ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc @@ -51173,6 +51365,9 @@ OUI:405662* OUI:405A9B* ID_OUI_FROM_DATABASE=ANOVO +OUI:405BD8* + ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD. + OUI:405CFD* ID_OUI_FROM_DATABASE=Dell Inc. @@ -51719,6 +51914,9 @@ OUI:440010* OUI:440049* ID_OUI_FROM_DATABASE=Amazon Technologies Inc. +OUI:44004D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:44032C* ID_OUI_FROM_DATABASE=Intel Corporate @@ -51746,6 +51944,9 @@ OUI:4411C2* OUI:441319* ID_OUI_FROM_DATABASE=WKK TECHNOLOGY LTD. +OUI:4413D0* + ID_OUI_FROM_DATABASE=zte corporation + OUI:441441* ID_OUI_FROM_DATABASE=AudioControl Inc. @@ -51917,6 +52118,9 @@ OUI:445829* OUI:44599F* ID_OUI_FROM_DATABASE=Criticare Systems, Inc +OUI:4459E3* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:445D5E* ID_OUI_FROM_DATABASE=SHENZHEN Coolkit Technology CO.,LTD @@ -52499,6 +52703,9 @@ OUI:484D7E* OUI:485073* ID_OUI_FROM_DATABASE=Microsoft Corporation +OUI:485169* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:4851B7* ID_OUI_FROM_DATABASE=Intel Corporate @@ -52637,6 +52844,9 @@ OUI:487583* OUI:487604* ID_OUI_FROM_DATABASE=Private +OUI:487746* + ID_OUI_FROM_DATABASE=Calix Inc. + OUI:487A55* ID_OUI_FROM_DATABASE=ALE International @@ -52748,6 +52958,9 @@ OUI:48A6B8* OUI:48A6D2* ID_OUI_FROM_DATABASE=GJsun Optical Science and Tech Co.,Ltd. +OUI:48A73C* + ID_OUI_FROM_DATABASE=Sichuan tianyi kanghe communications co., LTD + OUI:48A74E* ID_OUI_FROM_DATABASE=zte corporation @@ -52895,6 +53108,9 @@ OUI:48DF37* OUI:48E1AF* ID_OUI_FROM_DATABASE=Vity +OUI:48E1E9* + ID_OUI_FROM_DATABASE=Chengdu Meross Technology Co., Ltd. + OUI:48E244* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. @@ -53660,6 +53876,9 @@ OUI:4CE676* OUI:4CE933* ID_OUI_FROM_DATABASE=RailComm, LLC +OUI:4CE9E4* + ID_OUI_FROM_DATABASE=New H3C Technologies Co., Ltd + OUI:4CEB42* ID_OUI_FROM_DATABASE=Intel Corporate @@ -53816,6 +54035,9 @@ OUI:501AA5* OUI:501AC5* ID_OUI_FROM_DATABASE=Microsoft +OUI:501B32* + ID_OUI_FROM_DATABASE=Taicang T&W Electronics + OUI:501CB0* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -54167,6 +54389,9 @@ OUI:50934F* OUI:509551* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. +OUI:509744* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + OUI:509772* ID_OUI_FROM_DATABASE=Westinghouse Digital @@ -54284,6 +54509,9 @@ OUI:50AD92* OUI:50ADD5* ID_OUI_FROM_DATABASE=Dynalec Corporation +OUI:50AF4D* + ID_OUI_FROM_DATABASE=zte corporation + OUI:50AF73* ID_OUI_FROM_DATABASE=Shenzhen Bitland Information Technology Co., Ltd. @@ -54353,6 +54581,9 @@ OUI:50D274* OUI:50D37F* ID_OUI_FROM_DATABASE=Yu Fly Mikly Way Science and Technology Co., Ltd. +OUI:50D4F7* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:50D59C* ID_OUI_FROM_DATABASE=Thai Habel Industrial Co., Ltd. @@ -54380,6 +54611,9 @@ OUI:50DD4F* OUI:50DF95* ID_OUI_FROM_DATABASE=Lytx +OUI:50E085* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:50E0C7* ID_OUI_FROM_DATABASE=TurControlSystme AG @@ -54437,6 +54671,9 @@ OUI:50F61A* OUI:50F722* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:50F8A5* + ID_OUI_FROM_DATABASE=eWBM Co., Ltd. + OUI:50FA84* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. @@ -54812,6 +55049,9 @@ OUI:548998* OUI:548CA0* ID_OUI_FROM_DATABASE=Liteon Technology Corporation +OUI:549209* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:5492BE* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -54959,6 +55199,9 @@ OUI:54B802* OUI:54B80A* ID_OUI_FROM_DATABASE=D-Link International +OUI:54BAD6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:54BD79* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -55031,6 +55274,9 @@ OUI:54DF24* OUI:54DF63* ID_OUI_FROM_DATABASE=Intrakey technologies GmbH +OUI:54E019* + ID_OUI_FROM_DATABASE=Ring LLC + OUI:54E032* ID_OUI_FROM_DATABASE=Juniper Networks @@ -55070,12 +55316,18 @@ OUI:54E6FC* OUI:54EAA8* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:54EC2F* + ID_OUI_FROM_DATABASE=Ruckus Wireless + OUI:54EDA3* ID_OUI_FROM_DATABASE=Navdy, Inc. OUI:54EE75* ID_OUI_FROM_DATABASE=Wistron InfoComm(Kunshan)Co.,Ltd. +OUI:54EF44* + ID_OUI_FROM_DATABASE=Lumi United Technology Co., Ltd + OUI:54EF92* ID_OUI_FROM_DATABASE=Shenzhen Elink Technology Co., LTD @@ -55949,6 +56201,9 @@ OUI:5C5819* OUI:5C5948* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:5C5AC7* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:5C5AEA* ID_OUI_FROM_DATABASE=FORD @@ -56030,6 +56285,9 @@ OUI:5C86C1* OUI:5C8778* ID_OUI_FROM_DATABASE=Cybertelbridge co.,ltd +OUI:5C879C* + ID_OUI_FROM_DATABASE=Intel Corporate + OUI:5C8816* ID_OUI_FROM_DATABASE=Rockwell Automation @@ -56360,6 +56618,9 @@ OUI:5CF9DD* OUI:5CF9F0* ID_OUI_FROM_DATABASE=Atomos Engineering P/L +OUI:5CFAFB* + ID_OUI_FROM_DATABASE=Acubit + OUI:5CFB7C* ID_OUI_FROM_DATABASE=Shenzhen Jingxun Software Telecommunication Technology Co.,Ltd @@ -56474,6 +56735,9 @@ OUI:6024C1* OUI:60271C* ID_OUI_FROM_DATABASE=VIDEOR E. Hartig GmbH +OUI:6029D5* + ID_OUI_FROM_DATABASE=DAVOLINK Inc. + OUI:602A54* ID_OUI_FROM_DATABASE=CardioTek B.V. @@ -56519,6 +56783,9 @@ OUI:6038E0* OUI:60391F* ID_OUI_FROM_DATABASE=ABB Ltd +OUI:603A7C* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:603D26* ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. @@ -56723,6 +56990,51 @@ OUI:609217* OUI:6092F5* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. +OUI:6095CE0* + ID_OUI_FROM_DATABASE=Siema Applications + +OUI:6095CE1* + ID_OUI_FROM_DATABASE=Ponoor Experiments Inc. + +OUI:6095CE2* + ID_OUI_FROM_DATABASE=Q-SENTECH Co.,Ltd. + +OUI:6095CE3* + ID_OUI_FROM_DATABASE=Robot S.A. + +OUI:6095CE4* + ID_OUI_FROM_DATABASE=Untangle, Inc. + +OUI:6095CE5* + ID_OUI_FROM_DATABASE=AdvanWISE Corporation + +OUI:6095CE6* + ID_OUI_FROM_DATABASE=Xiamen Sigmastar Technology Ltd. + +OUI:6095CE7* + ID_OUI_FROM_DATABASE=Cadmo Soluciones SAC + +OUI:6095CE8* + ID_OUI_FROM_DATABASE=Trophy SAS + +OUI:6095CE9* + ID_OUI_FROM_DATABASE=Jlztlink Industry(ShenZhen)Co.,Ltd. + +OUI:6095CEA* + ID_OUI_FROM_DATABASE=(UN)MANNED + +OUI:6095CEB* + ID_OUI_FROM_DATABASE=Beijing Sinomedisite Bio-tech Co.,Ltd + +OUI:6095CEC* + ID_OUI_FROM_DATABASE=Synamedia + +OUI:6095CED* + ID_OUI_FROM_DATABASE=GovComm + +OUI:6095CEE* + ID_OUI_FROM_DATABASE=VNS Inc. + OUI:609620* ID_OUI_FROM_DATABASE=Private @@ -56873,6 +57185,9 @@ OUI:60D1AA* OUI:60D21C* ID_OUI_FROM_DATABASE=Sunnovo International Limited +OUI:60D248* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:60D262* ID_OUI_FROM_DATABASE=Tzukuri Pty Ltd @@ -57836,6 +58151,9 @@ OUI:683E34* OUI:683EEC* ID_OUI_FROM_DATABASE=ERECA +OUI:683F1E* + ID_OUI_FROM_DATABASE=EFFECT Photonics B.V. + OUI:684352* ID_OUI_FROM_DATABASE=Bhuu Limited @@ -57920,6 +58238,9 @@ OUI:686975* OUI:6869F2* ID_OUI_FROM_DATABASE=ComAp s.r.o. +OUI:686DBC* + ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. + OUI:686E23* ID_OUI_FROM_DATABASE=Wi3 Inc. @@ -57953,6 +58274,9 @@ OUI:687CC8* OUI:687CD5* ID_OUI_FROM_DATABASE=Y Soft Corporation, a.s. +OUI:687D6B* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:687F74* ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC @@ -58218,7 +58542,7 @@ OUI:68DB54* ID_OUI_FROM_DATABASE=Phicomm (Shanghai) Co., Ltd. OUI:68DB67* - ID_OUI_FROM_DATABASE=Nantong Coship Electronics Co., Ltd + ID_OUI_FROM_DATABASE=Nantong Coship Electronics Co., Ltd. OUI:68DB96* ID_OUI_FROM_DATABASE=OPWILL Technologies CO .,LTD @@ -58388,6 +58712,9 @@ OUI:6C25B9* OUI:6C2779* ID_OUI_FROM_DATABASE=Microsoft Mobile Oy +OUI:6C2990* + ID_OUI_FROM_DATABASE=WiZ Connected Lighting Company Limited + OUI:6C2995* ID_OUI_FROM_DATABASE=Intel Corporate @@ -58562,6 +58889,9 @@ OUI:6C5CDE* OUI:6C5D63* ID_OUI_FROM_DATABASE=ShenZhen Rapoo Technology Co., Ltd. +OUI:6C5E3B* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:6C5E7A* ID_OUI_FROM_DATABASE=Ubiquitous Internet Telecom Co., Ltd @@ -58628,6 +58958,9 @@ OUI:6C8686* OUI:6C8814* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:6C8AEC* + ID_OUI_FROM_DATABASE=Nantong Coship Electronics Co., Ltd. + OUI:6C8B2F* ID_OUI_FROM_DATABASE=zte corporation @@ -58721,6 +59054,9 @@ OUI:6CA96F* OUI:6CAAB3* ID_OUI_FROM_DATABASE=Ruckus Wireless +OUI:6CAB05* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:6CAB31* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -58812,7 +59148,7 @@ OUI:6CD032* ID_OUI_FROM_DATABASE=LG Electronics OUI:6CD146* - ID_OUI_FROM_DATABASE=Smartek d.o.o. + ID_OUI_FROM_DATABASE=FRAMOS GmbH OUI:6CD1B0* ID_OUI_FROM_DATABASE=WING SING ELECTRONICS HONG KONG LIMITED @@ -58913,6 +59249,9 @@ OUI:6CEFC6* OUI:6CF049* ID_OUI_FROM_DATABASE=GIGA-BYTE TECHNOLOGY CO.,LTD. +OUI:6CF17E* + ID_OUI_FROM_DATABASE=Zhejiang Uniview Technologies Co.,Ltd. + OUI:6CF373* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -58955,6 +59294,9 @@ OUI:700258* OUI:70037E* ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. +OUI:700433* + ID_OUI_FROM_DATABASE=California Things Inc. + OUI:700514* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) @@ -59594,6 +59936,9 @@ OUI:70B3D5015* OUI:70B3D5016* ID_OUI_FROM_DATABASE=Guardian Controls International Ltd +OUI:70B3D5019* + ID_OUI_FROM_DATABASE=Transit Solutions, LLC. + OUI:70B3D501A* ID_OUI_FROM_DATABASE=Cubro Acronet GesmbH @@ -59741,6 +60086,9 @@ OUI:70B3D5059* OUI:70B3D505A* ID_OUI_FROM_DATABASE=Uni Control System Sp. z o. o. +OUI:70B3D505B* + ID_OUI_FROM_DATABASE=PAL Inc. + OUI:70B3D505C* ID_OUI_FROM_DATABASE=Amber Kinetics Inc @@ -59765,6 +60113,9 @@ OUI:70B3D5063* OUI:70B3D5066* ID_OUI_FROM_DATABASE=North Pole Engineering, Inc. +OUI:70B3D5068* + ID_OUI_FROM_DATABASE=Onethinx BV + OUI:70B3D5069* ID_OUI_FROM_DATABASE=ONDEMAND LABORATORY Co., Ltd. @@ -59816,6 +60167,9 @@ OUI:70B3D507E* OUI:70B3D507F* ID_OUI_FROM_DATABASE=Abalance Corporation +OUI:70B3D5080* + ID_OUI_FROM_DATABASE=ABB + OUI:70B3D5081* ID_OUI_FROM_DATABASE=IST Technologies (SHENZHEN) Limited @@ -59864,6 +60218,9 @@ OUI:70B3D5091* OUI:70B3D5092* ID_OUI_FROM_DATABASE=inomed Medizintechnik GmbH +OUI:70B3D5093* + ID_OUI_FROM_DATABASE=Legrand Electric Ltd + OUI:70B3D5094* ID_OUI_FROM_DATABASE=Circuitlink Pty Ltd @@ -60008,6 +60365,9 @@ OUI:70B3D50C8* OUI:70B3D50C9* ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD., +OUI:70B3D50CA* + ID_OUI_FROM_DATABASE=VITEC + OUI:70B3D50CD* ID_OUI_FROM_DATABASE=AML Oceanographic @@ -60128,6 +60488,9 @@ OUI:70B3D50FF* OUI:70B3D5100* ID_OUI_FROM_DATABASE=Gupsy GmbH +OUI:70B3D5102* + ID_OUI_FROM_DATABASE=Oxford Monitoring Solutions Ltd + OUI:70B3D5103* ID_OUI_FROM_DATABASE=HANYOUNG NUX CO.,LTD @@ -60161,6 +60524,9 @@ OUI:70B3D510E* OUI:70B3D510F* ID_OUI_FROM_DATABASE=neQis +OUI:70B3D5111* + ID_OUI_FROM_DATABASE=Leonardo Sistemi Integrati S.r.l. + OUI:70B3D5112* ID_OUI_FROM_DATABASE=DiTEST Fahrzeugdiagnose GmbH @@ -60311,6 +60677,9 @@ OUI:70B3D514F* OUI:70B3D5150* ID_OUI_FROM_DATABASE=YUYAMA MFG Co.,Ltd +OUI:70B3D5151* + ID_OUI_FROM_DATABASE=Virsae Group Ltd + OUI:70B3D5152* ID_OUI_FROM_DATABASE=Xped Corporation Pty Ltd @@ -60650,9 +61019,15 @@ OUI:70B3D51F4* OUI:70B3D51F5* ID_OUI_FROM_DATABASE=Martec S.p.A. +OUI:70B3D51F7* + ID_OUI_FROM_DATABASE=Morgan Schaffer Inc. + OUI:70B3D51F8* ID_OUI_FROM_DATABASE=Convergent Design +OUI:70B3D51F9* + ID_OUI_FROM_DATABASE=Automata GmbH & Co. KG + OUI:70B3D51FD* ID_OUI_FROM_DATABASE=BRS Sistemas Eletrônicos @@ -60701,6 +61076,9 @@ OUI:70B3D520E* OUI:70B3D520F* ID_OUI_FROM_DATABASE=Tieline Research Pty Ltd +OUI:70B3D5210* + ID_OUI_FROM_DATABASE=Eastone Century Technology Co.,Ltd. + OUI:70B3D5211* ID_OUI_FROM_DATABASE=Fracarro srl @@ -60797,6 +61175,9 @@ OUI:70B3D5235* OUI:70B3D5236* ID_OUI_FROM_DATABASE=Monnit Corporation +OUI:70B3D5237* + ID_OUI_FROM_DATABASE=Sikom AS + OUI:70B3D5238* ID_OUI_FROM_DATABASE=Arete Associates @@ -60842,6 +61223,9 @@ OUI:70B3D524A* OUI:70B3D524B* ID_OUI_FROM_DATABASE=TOSEI ENGINEERING CORP. +OUI:70B3D524C* + ID_OUI_FROM_DATABASE=Astronomical Research Cameras, Inc. + OUI:70B3D524D* ID_OUI_FROM_DATABASE=INFO CREATIVE (HK) LTD @@ -60872,6 +61256,9 @@ OUI:70B3D5255* OUI:70B3D5257* ID_OUI_FROM_DATABASE=LG Electronics +OUI:70B3D5258* + ID_OUI_FROM_DATABASE=BAYKON Endüstriyel Kontrol Sistemleri San. ve Tic. A.Ş. + OUI:70B3D5259* ID_OUI_FROM_DATABASE=Zebra Elektronik A.S. @@ -61106,12 +61493,18 @@ OUI:70B3D52BE* OUI:70B3D52BF* ID_OUI_FROM_DATABASE=FOSHAN VOHOM +OUI:70B3D52C0* + ID_OUI_FROM_DATABASE=Sensative AB + OUI:70B3D52C2* ID_OUI_FROM_DATABASE=Quantum Detectors OUI:70B3D52C3* ID_OUI_FROM_DATABASE=Proterra +OUI:70B3D52C7* + ID_OUI_FROM_DATABASE=Worldsensing + OUI:70B3D52C9* ID_OUI_FROM_DATABASE=SEASON DESIGN TECHNOLOGY @@ -61145,6 +61538,9 @@ OUI:70B3D52D5* OUI:70B3D52D6* ID_OUI_FROM_DATABASE=Kvazar LLC +OUI:70B3D52D7* + ID_OUI_FROM_DATABASE=Private + OUI:70B3D52DA* ID_OUI_FROM_DATABASE=Skywave Networks Private Limited @@ -61292,6 +61688,9 @@ OUI:70B3D531B* OUI:70B3D531C* ID_OUI_FROM_DATABASE=FINANCIERE DE L'OMBREE (eolane) +OUI:70B3D531D* + ID_OUI_FROM_DATABASE=AVA Monitoring AB + OUI:70B3D531E* ID_OUI_FROM_DATABASE=GILLAM-FEI S.A. @@ -61508,6 +61907,9 @@ OUI:70B3D537C* OUI:70B3D537D* ID_OUI_FROM_DATABASE=The DX Shop Limited +OUI:70B3D537E* + ID_OUI_FROM_DATABASE=ELINKGATE JSC + OUI:70B3D537F* ID_OUI_FROM_DATABASE=IDS Innomic GmbH @@ -61628,6 +62030,9 @@ OUI:70B3D53BB* OUI:70B3D53BC* ID_OUI_FROM_DATABASE=SciTronix +OUI:70B3D53BD* + ID_OUI_FROM_DATABASE=DAO QIN TECHNOLOGY CO.LTD. + OUI:70B3D53BE* ID_OUI_FROM_DATABASE=MyDefence Communication ApS @@ -61661,6 +62066,9 @@ OUI:70B3D53C9* OUI:70B3D53CA* ID_OUI_FROM_DATABASE=TTI Ltd +OUI:70B3D53CB* + ID_OUI_FROM_DATABASE=GeoSpectrum Technologies Inc + OUI:70B3D53CC* ID_OUI_FROM_DATABASE=TerOpta Ltd @@ -61802,6 +62210,9 @@ OUI:70B3D5408* OUI:70B3D540A* ID_OUI_FROM_DATABASE=Monroe Electronics, Inc. +OUI:70B3D540B* + ID_OUI_FROM_DATABASE=QUERCUS TECHNOLOGIES, S.L. + OUI:70B3D540E* ID_OUI_FROM_DATABASE=Liaoyun Information Technology Co., Ltd. @@ -61854,7 +62265,7 @@ OUI:70B3D542C* ID_OUI_FROM_DATABASE=D.Marchiori Srl OUI:70B3D542D* - ID_OUI_FROM_DATABASE=RCH Italia SpA + ID_OUI_FROM_DATABASE=RCH ITALIA SPA OUI:70B3D542E* ID_OUI_FROM_DATABASE=Dr. Zinngrebe GmbH @@ -61913,6 +62324,9 @@ OUI:70B3D5442* OUI:70B3D5443* ID_OUI_FROM_DATABASE=Slot3 GmbH +OUI:70B3D5444* + ID_OUI_FROM_DATABASE=AMS Controls, Inc. + OUI:70B3D5445* ID_OUI_FROM_DATABASE=Advanced Devices SpA @@ -61934,6 +62348,9 @@ OUI:70B3D544E* OUI:70B3D5455* ID_OUI_FROM_DATABASE=Heartlandmicropayments +OUI:70B3D5456* + ID_OUI_FROM_DATABASE=Technological Application and Production One Member Liability Company (Tecapro company) + OUI:70B3D5457* ID_OUI_FROM_DATABASE=Vivaldi Clima Srl @@ -61964,6 +62381,9 @@ OUI:70B3D5461* OUI:70B3D5462* ID_OUI_FROM_DATABASE=EarTex +OUI:70B3D5463* + ID_OUI_FROM_DATABASE=WARECUBE,INC + OUI:70B3D5465* ID_OUI_FROM_DATABASE=ENERGISME @@ -62006,6 +62426,9 @@ OUI:70B3D5478* OUI:70B3D5479* ID_OUI_FROM_DATABASE=LINEAGE POWER PVT LTD., +OUI:70B3D547A* + ID_OUI_FROM_DATABASE=GlooVir Inc. + OUI:70B3D547C* ID_OUI_FROM_DATABASE=Par-Tech, Inc. @@ -62099,6 +62522,9 @@ OUI:70B3D54A6* OUI:70B3D54A7* ID_OUI_FROM_DATABASE=aelettronica group srl +OUI:70B3D54A8* + ID_OUI_FROM_DATABASE=Acrodea, Inc. + OUI:70B3D54A9* ID_OUI_FROM_DATABASE=WARECUBE,INC @@ -62183,6 +62609,9 @@ OUI:70B3D54C7* OUI:70B3D54C8* ID_OUI_FROM_DATABASE=Hosokawa Micron Powder Systems +OUI:70B3D54CC* + ID_OUI_FROM_DATABASE=FRESENIUS MEDICAL CARE + OUI:70B3D54CD* ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. @@ -62273,6 +62702,9 @@ OUI:70B3D54F8* OUI:70B3D54F9* ID_OUI_FROM_DATABASE=OptoPrecision GmbH +OUI:70B3D54FA* + ID_OUI_FROM_DATABASE=Thruvision Limited + OUI:70B3D54FC* ID_OUI_FROM_DATABASE=Mettler Toledo @@ -62297,12 +62729,18 @@ OUI:70B3D5504* OUI:70B3D5505* ID_OUI_FROM_DATABASE=MC2-Technologies +OUI:70B3D5506* + ID_OUI_FROM_DATABASE=Tonbo Imaging Pte Ltd + OUI:70B3D5507* ID_OUI_FROM_DATABASE=Human Oriented Technology, Inc. OUI:70B3D5508* ID_OUI_FROM_DATABASE=INSEVIS GmbH +OUI:70B3D550A* + ID_OUI_FROM_DATABASE=AMEDTEC Medizintechnik Aue GmbH + OUI:70B3D550E* ID_OUI_FROM_DATABASE=Micro Trend Automation Co., LTD @@ -62453,6 +62891,9 @@ OUI:70B3D5554* OUI:70B3D5555* ID_OUI_FROM_DATABASE=SoftLab-NSK +OUI:70B3D5556* + ID_OUI_FROM_DATABASE=OHASHI ENGINEERING CO.,LTD. + OUI:70B3D5557* ID_OUI_FROM_DATABASE=HEITEC AG @@ -62498,6 +62939,9 @@ OUI:70B3D556C* OUI:70B3D5570* ID_OUI_FROM_DATABASE=Bayern Engineering GmbH & Co. KG +OUI:70B3D5571* + ID_OUI_FROM_DATABASE=Echogear + OUI:70B3D5572* ID_OUI_FROM_DATABASE=CRDE @@ -62525,6 +62969,9 @@ OUI:70B3D557C* OUI:70B3D557D* ID_OUI_FROM_DATABASE=WICOM1 GmbH +OUI:70B3D557E* + ID_OUI_FROM_DATABASE=Ascon Tecnologic S.r.l. + OUI:70B3D557F* ID_OUI_FROM_DATABASE=MBio Diagnostics, Inc. @@ -62612,6 +63059,9 @@ OUI:70B3D55A3* OUI:70B3D55A5* ID_OUI_FROM_DATABASE=Rehwork GmbH +OUI:70B3D55A6* + ID_OUI_FROM_DATABASE=TimeMachines Inc. + OUI:70B3D55A7* ID_OUI_FROM_DATABASE=ABB S.p.A. @@ -62642,6 +63092,9 @@ OUI:70B3D55B6* OUI:70B3D55B8* ID_OUI_FROM_DATABASE=Hella Gutmann Solutions GmbH +OUI:70B3D55BA* + ID_OUI_FROM_DATABASE=INFRASAFE/ ADVANTOR SYSTEMS + OUI:70B3D55BC* ID_OUI_FROM_DATABASE=LAMTEC Meß- und Regeltechnik für Feuerungen GmbH & Co. KG @@ -62708,6 +63161,9 @@ OUI:70B3D55DE* OUI:70B3D55E0* ID_OUI_FROM_DATABASE=Hexagon Metrology SAS +OUI:70B3D55E1* + ID_OUI_FROM_DATABASE=Arevita + OUI:70B3D55E2* ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG @@ -62984,6 +63440,9 @@ OUI:70B3D565C* OUI:70B3D565D* ID_OUI_FROM_DATABASE=GEGA ELECTRONIQUE +OUI:70B3D565E* + ID_OUI_FROM_DATABASE=Season Electronics Ltd + OUI:70B3D5660* ID_OUI_FROM_DATABASE=Smart Service Technologies CO., LTD @@ -63287,6 +63746,9 @@ OUI:70B3D56FC* OUI:70B3D56FD* ID_OUI_FROM_DATABASE=Core Akıllı Ev Sistemleri +OUI:70B3D56FE* + ID_OUI_FROM_DATABASE=NTO IRE-POLUS + OUI:70B3D56FF* ID_OUI_FROM_DATABASE=AKEO PLUS @@ -63371,6 +63833,9 @@ OUI:70B3D5723* OUI:70B3D5724* ID_OUI_FROM_DATABASE=Quan International Co., Ltd. +OUI:70B3D5726* + ID_OUI_FROM_DATABASE=ATGS + OUI:70B3D5727* ID_OUI_FROM_DATABASE=LP Technologies Inc. @@ -63755,6 +64220,9 @@ OUI:70B3D57C8* OUI:70B3D57C9* ID_OUI_FROM_DATABASE=Council Rock +OUI:70B3D57CB* + ID_OUI_FROM_DATABASE=KeyW Corporation + OUI:70B3D57CD* ID_OUI_FROM_DATABASE=Molekuler Goruntuleme A.S. @@ -63876,7 +64344,7 @@ OUI:70B3D57FD* ID_OUI_FROM_DATABASE=SYS TEC electronic GmbH OUI:70B3D57FE* - ID_OUI_FROM_DATABASE=RCH Italia SpA + ID_OUI_FROM_DATABASE=RCH ITALIA SPA OUI:70B3D5800* ID_OUI_FROM_DATABASE=HeadsafeIP PTY LTD @@ -63953,6 +64421,9 @@ OUI:70B3D5820* OUI:70B3D5821* ID_OUI_FROM_DATABASE=HL2 group +OUI:70B3D5822* + ID_OUI_FROM_DATABASE=Angora Networks + OUI:70B3D5823* ID_OUI_FROM_DATABASE=SP Controls @@ -63998,6 +64469,9 @@ OUI:70B3D5838* OUI:70B3D5839* ID_OUI_FROM_DATABASE=Rockwell Collins Canada +OUI:70B3D583A* + ID_OUI_FROM_DATABASE=EMDEP CENTRO TECNOLOGICO MEXICO + OUI:70B3D583B* ID_OUI_FROM_DATABASE=Telefonix Incorporated @@ -64065,7 +64539,7 @@ OUI:70B3D5855* ID_OUI_FROM_DATABASE=CRDE OUI:70B3D5857* - ID_OUI_FROM_DATABASE=RCH Italia SpA + ID_OUI_FROM_DATABASE=RCH ITALIA SPA OUI:70B3D585A* ID_OUI_FROM_DATABASE=BRUSHIES @@ -64163,6 +64637,9 @@ OUI:70B3D5881* OUI:70B3D5882* ID_OUI_FROM_DATABASE=SIMON TECH, S.L. +OUI:70B3D5884* + ID_OUI_FROM_DATABASE=LG Electronics + OUI:70B3D5885* ID_OUI_FROM_DATABASE=QuirkLogic @@ -64283,6 +64760,9 @@ OUI:70B3D58C2* OUI:70B3D58C3* ID_OUI_FROM_DATABASE=Wyebot, Inc. +OUI:70B3D58C4* + ID_OUI_FROM_DATABASE=APE GmbH + OUI:70B3D58C5* ID_OUI_FROM_DATABASE=HMicro Inc @@ -64331,6 +64811,9 @@ OUI:70B3D58DB* OUI:70B3D58DC* ID_OUI_FROM_DATABASE=Niveo International BV +OUI:70B3D58DF* + ID_OUI_FROM_DATABASE=DORLET SAU + OUI:70B3D58E0* ID_OUI_FROM_DATABASE=SOUDAX EQUIPEMENTS @@ -64352,6 +64835,9 @@ OUI:70B3D58EB* OUI:70B3D58EC* ID_OUI_FROM_DATABASE=Rudy Tellert +OUI:70B3D58ED* + ID_OUI_FROM_DATABASE=NanoSense + OUI:70B3D58EE* ID_OUI_FROM_DATABASE=Network Additions @@ -64667,6 +65153,9 @@ OUI:70B3D597F* OUI:70B3D5981* ID_OUI_FROM_DATABASE=Zamir Recognition Systems Ltd. +OUI:70B3D5982* + ID_OUI_FROM_DATABASE=3S - Sensors, Signal Processing, Systems GmbH + OUI:70B3D5984* ID_OUI_FROM_DATABASE=Sanmina Israel @@ -64724,6 +65213,9 @@ OUI:70B3D5999* OUI:70B3D599A* ID_OUI_FROM_DATABASE=KEVIC. inc, +OUI:70B3D599B* + ID_OUI_FROM_DATABASE=RCH ITALIA SPA + OUI:70B3D599C* ID_OUI_FROM_DATABASE=Enerwise Solutions Ltd. @@ -64796,6 +65288,9 @@ OUI:70B3D59BD* OUI:70B3D59BE* ID_OUI_FROM_DATABASE=Izome +OUI:70B3D59BF* + ID_OUI_FROM_DATABASE=Xiris Automation Inc. + OUI:70B3D59C0* ID_OUI_FROM_DATABASE=Schneider Displaytechnik GmbH @@ -64859,6 +65354,9 @@ OUI:70B3D59D7* OUI:70B3D59D9* ID_OUI_FROM_DATABASE=ATX Networks Corp +OUI:70B3D59DA* + ID_OUI_FROM_DATABASE=Blake UK + OUI:70B3D59DB* ID_OUI_FROM_DATABASE=CAS Medical Systems, Inc @@ -65033,6 +65531,9 @@ OUI:70B3D5A29* OUI:70B3D5A2A* ID_OUI_FROM_DATABASE=Redwood Systems +OUI:70B3D5A2B* + ID_OUI_FROM_DATABASE=Clever Devices + OUI:70B3D5A2C* ID_OUI_FROM_DATABASE=TLV CO., LTD. @@ -65078,12 +65579,18 @@ OUI:70B3D5A3F* OUI:70B3D5A40* ID_OUI_FROM_DATABASE=STRACK LIFT AUTOMATION GmbH +OUI:70B3D5A42* + ID_OUI_FROM_DATABASE=iMAR Navigation GmbH + OUI:70B3D5A43* ID_OUI_FROM_DATABASE=OLEDCOMM OUI:70B3D5A44* ID_OUI_FROM_DATABASE=FSR Inc +OUI:70B3D5A45* + ID_OUI_FROM_DATABASE=Viper Innovations Ltd + OUI:70B3D5A46* ID_OUI_FROM_DATABASE=Foxconn 4Tech @@ -65102,6 +65609,9 @@ OUI:70B3D5A4B* OUI:70B3D5A4C* ID_OUI_FROM_DATABASE=Alere Technologies AS +OUI:70B3D5A4D* + ID_OUI_FROM_DATABASE=LANSITEC TECHNOLOGY CO., LTD + OUI:70B3D5A4E* ID_OUI_FROM_DATABASE=Array Technologies Inc. @@ -65360,12 +65870,18 @@ OUI:70B3D5ABB* OUI:70B3D5ABC* ID_OUI_FROM_DATABASE=BKM-Micronic Richtfunkanlagen GmbH +OUI:70B3D5ABD* + ID_OUI_FROM_DATABASE=wtec GmbH + OUI:70B3D5ABE* ID_OUI_FROM_DATABASE=MART NETWORK SOLUTIONS LTD OUI:70B3D5ABF* ID_OUI_FROM_DATABASE=AGR International +OUI:70B3D5AC0* + ID_OUI_FROM_DATABASE=RITEC + OUI:70B3D5AC1* ID_OUI_FROM_DATABASE=AEM Singapore Pte. Ltd. @@ -65525,6 +66041,9 @@ OUI:70B3D5B04* OUI:70B3D5B05* ID_OUI_FROM_DATABASE=E-PLUS TECHNOLOGY CO., LTD +OUI:70B3D5B06* + ID_OUI_FROM_DATABASE=MULTIVOICE LLC + OUI:70B3D5B07* ID_OUI_FROM_DATABASE=Arrowvale Electronics @@ -65576,6 +66095,9 @@ OUI:70B3D5B1E* OUI:70B3D5B1F* ID_OUI_FROM_DATABASE=TECNOWATT +OUI:70B3D5B20* + ID_OUI_FROM_DATABASE=ICT BUSINESS GROUP of Humanrights Center for disabled people + OUI:70B3D5B21* ID_OUI_FROM_DATABASE=TATTILE SRL @@ -65606,6 +66128,9 @@ OUI:70B3D5B2D* OUI:70B3D5B2E* ID_OUI_FROM_DATABASE=Green Access Ltd +OUI:70B3D5B2F* + ID_OUI_FROM_DATABASE=Hermann Automation GmbH + OUI:70B3D5B30* ID_OUI_FROM_DATABASE=Systolé Hardware B.V. @@ -65681,6 +66206,9 @@ OUI:70B3D5B50* OUI:70B3D5B51* ID_OUI_FROM_DATABASE=Critical Link LLC +OUI:70B3D5B52* + ID_OUI_FROM_DATABASE=AEye, Inc. + OUI:70B3D5B53* ID_OUI_FROM_DATABASE=Revolution Retail Systems, LLC @@ -65690,6 +66218,9 @@ OUI:70B3D5B55* OUI:70B3D5B56* ID_OUI_FROM_DATABASE=Power Electronics Espana, S.L. +OUI:70B3D5B58* + ID_OUI_FROM_DATABASE=INTERNET PROTOCOLO LOGICA SL + OUI:70B3D5B59* ID_OUI_FROM_DATABASE=FutureTechnologyLaboratories INC. @@ -65735,6 +66266,9 @@ OUI:70B3D5B71* OUI:70B3D5B72* ID_OUI_FROM_DATABASE=UB330.net d.o.o. +OUI:70B3D5B73* + ID_OUI_FROM_DATABASE=Cetto Industries + OUI:70B3D5B74* ID_OUI_FROM_DATABASE=OnYield Inc Ltd @@ -65882,6 +66416,9 @@ OUI:70B3D5BB3* OUI:70B3D5BB4* ID_OUI_FROM_DATABASE=Integritech +OUI:70B3D5BB5* + ID_OUI_FROM_DATABASE=Grossenbacher Systeme AG + OUI:70B3D5BB6* ID_OUI_FROM_DATABASE=Franke Aquarotter GmbH @@ -65924,6 +66461,9 @@ OUI:70B3D5BC5* OUI:70B3D5BC6* ID_OUI_FROM_DATABASE=Hatteland Display AS +OUI:70B3D5BC9* + ID_OUI_FROM_DATABASE=Yite technology + OUI:70B3D5BCA* ID_OUI_FROM_DATABASE=Deymed Diagnostic @@ -65987,6 +66527,9 @@ OUI:70B3D5BE5* OUI:70B3D5BE6* ID_OUI_FROM_DATABASE=CCII Systems (Pty) Ltd +OUI:70B3D5BE7* + ID_OUI_FROM_DATABASE=Syscom Instruments SA + OUI:70B3D5BE8* ID_OUI_FROM_DATABASE=AndFun Co.,Ltd. @@ -66035,12 +66578,18 @@ OUI:70B3D5BFB* OUI:70B3D5BFE* ID_OUI_FROM_DATABASE=Aplex Technology Inc. +OUI:70B3D5C00* + ID_OUI_FROM_DATABASE=BESO sp. z o.o. + OUI:70B3D5C01* ID_OUI_FROM_DATABASE=SmartGuard LLC OUI:70B3D5C03* ID_OUI_FROM_DATABASE=XAVi Technologies Corp. +OUI:70B3D5C04* + ID_OUI_FROM_DATABASE=Prolan Zrt. + OUI:70B3D5C05* ID_OUI_FROM_DATABASE=KST technology @@ -66209,6 +66758,9 @@ OUI:70B3D5C4D* OUI:70B3D5C4F* ID_OUI_FROM_DATABASE=AE Van de Vliet BVBA +OUI:70B3D5C51* + ID_OUI_FROM_DATABASE=Innotas Elektronik GmbH + OUI:70B3D5C53* ID_OUI_FROM_DATABASE=S Labs sp. z o.o. @@ -66236,6 +66788,9 @@ OUI:70B3D5C5C* OUI:70B3D5C5D* ID_OUI_FROM_DATABASE=FOSHAN SHILANTIAN NETWORK S.T. CO., LTD. +OUI:70B3D5C5F* + ID_OUI_FROM_DATABASE=Clean-Lasersysteme GmbH + OUI:70B3D5C60* ID_OUI_FROM_DATABASE=Gogo BA @@ -66443,6 +66998,9 @@ OUI:70B3D5CC3* OUI:70B3D5CC5* ID_OUI_FROM_DATABASE=Intecom +OUI:70B3D5CC6* + ID_OUI_FROM_DATABASE=MB connect line GmbH Fernwartungssysteme + OUI:70B3D5CC8* ID_OUI_FROM_DATABASE=PROFEN COMMUNICATIONS @@ -66485,6 +67043,9 @@ OUI:70B3D5CD5* OUI:70B3D5CD6* ID_OUI_FROM_DATABASE=VideoRay LLC +OUI:70B3D5CD7* + ID_OUI_FROM_DATABASE=AutomationX GmbH + OUI:70B3D5CD9* ID_OUI_FROM_DATABASE=Peter Huber Kaeltemaschinenbau GmbH @@ -66548,6 +67109,9 @@ OUI:70B3D5CF5* OUI:70B3D5CF6* ID_OUI_FROM_DATABASE=Tornado Modular Systems +OUI:70B3D5CF8* + ID_OUI_FROM_DATABASE=Idneo Technologies S.A.U. + OUI:70B3D5CFD* ID_OUI_FROM_DATABASE=iLOQ Oy @@ -66695,6 +67259,9 @@ OUI:70B3D5D47* OUI:70B3D5D48* ID_OUI_FROM_DATABASE=HEADROOM Broadcast GmbH +OUI:70B3D5D49* + ID_OUI_FROM_DATABASE=Sicon srl + OUI:70B3D5D4A* ID_OUI_FROM_DATABASE=OÜ ELIKO Tehnoloogia Arenduskeskus @@ -66725,6 +67292,9 @@ OUI:70B3D5D54* OUI:70B3D5D55* ID_OUI_FROM_DATABASE=WM Design s.r.o +OUI:70B3D5D56* + ID_OUI_FROM_DATABASE=KRONOTECH SRL + OUI:70B3D5D57* ID_OUI_FROM_DATABASE=TRIUMPH BOARD a.s. @@ -66965,6 +67535,9 @@ OUI:70B3D5DC0* OUI:70B3D5DC2* ID_OUI_FROM_DATABASE=SwineTech, Inc. +OUI:70B3D5DC3* + ID_OUI_FROM_DATABASE=Fath Mechatronics + OUI:70B3D5DC5* ID_OUI_FROM_DATABASE=Excel Medical Electronics LLC @@ -67064,6 +67637,9 @@ OUI:70B3D5DF2* OUI:70B3D5DF3* ID_OUI_FROM_DATABASE=SPC Bioclinicum +OUI:70B3D5DF4* + ID_OUI_FROM_DATABASE=Heim- & Bürokommunikation Ilmert e.K. + OUI:70B3D5DF5* ID_OUI_FROM_DATABASE=Beijing Huanyu Zhilian Science &Technology Co., Ltd. @@ -67082,12 +67658,18 @@ OUI:70B3D5DF9* OUI:70B3D5DFA* ID_OUI_FROM_DATABASE=Newtouch Electronics (Shanghai) Co.,Ltd. +OUI:70B3D5DFB* + ID_OUI_FROM_DATABASE=Yamamoto Works Ltd. + OUI:70B3D5DFC* ID_OUI_FROM_DATABASE=ELECTRONIC SYSTEMS DESIGN SPRL OUI:70B3D5DFD* ID_OUI_FROM_DATABASE=Contiweb +OUI:70B3D5DFE* + ID_OUI_FROM_DATABASE=microtec Sicherheitstechnik GmbH + OUI:70B3D5DFF* ID_OUI_FROM_DATABASE=Spanawave Corporation @@ -67121,6 +67703,9 @@ OUI:70B3D5E0D* OUI:70B3D5E0F* ID_OUI_FROM_DATABASE=Vtron Pty Ltd +OUI:70B3D5E14* + ID_OUI_FROM_DATABASE=Automata Spa + OUI:70B3D5E15* ID_OUI_FROM_DATABASE=Benetel @@ -67142,6 +67727,9 @@ OUI:70B3D5E1B* OUI:70B3D5E1C* ID_OUI_FROM_DATABASE=Xcenter AS +OUI:70B3D5E1E* + ID_OUI_FROM_DATABASE=Umano Medical Inc. + OUI:70B3D5E1F* ID_OUI_FROM_DATABASE=THETA432 @@ -67157,6 +67745,9 @@ OUI:70B3D5E22* OUI:70B3D5E23* ID_OUI_FROM_DATABASE=Smith Meter, Inc. +OUI:70B3D5E25* + ID_OUI_FROM_DATABASE=GJD Manufacturing + OUI:70B3D5E26* ID_OUI_FROM_DATABASE=FEITIAN CO.,LTD. @@ -67259,6 +67850,9 @@ OUI:70B3D5E52* OUI:70B3D5E53* ID_OUI_FROM_DATABASE=MI INC. +OUI:70B3D5E54* + ID_OUI_FROM_DATABASE=Beijing PanGu Company + OUI:70B3D5E55* ID_OUI_FROM_DATABASE=BELT S.r.l. @@ -67352,6 +67946,9 @@ OUI:70B3D5E86* OUI:70B3D5E88* ID_OUI_FROM_DATABASE=Breas Medical AB +OUI:70B3D5E8A* + ID_OUI_FROM_DATABASE=Melecs EWS GmbH + OUI:70B3D5E8C* ID_OUI_FROM_DATABASE=Fracarro srl @@ -67397,6 +67994,9 @@ OUI:70B3D5E9B* OUI:70B3D5E9C* ID_OUI_FROM_DATABASE=ATG UV Technology +OUI:70B3D5E9D* + ID_OUI_FROM_DATABASE=INTECH + OUI:70B3D5E9E* ID_OUI_FROM_DATABASE=MSB Elektronik und Gerätebau GmbH @@ -67430,6 +68030,9 @@ OUI:70B3D5EAB* OUI:70B3D5EAC* ID_OUI_FROM_DATABASE=Kentech Instruments Limited +OUI:70B3D5EAD* + ID_OUI_FROM_DATABASE=Cobo, Inc. + OUI:70B3D5EAE* ID_OUI_FROM_DATABASE=Orlaco Products B.V. @@ -67598,6 +68201,9 @@ OUI:70B3D5EFB* OUI:70B3D5EFE* ID_OUI_FROM_DATABASE=MEIDEN SYSTEM SOLUTIONS +OUI:70B3D5EFF* + ID_OUI_FROM_DATABASE=Carlo Gavazzi Industri + OUI:70B3D5F00* ID_OUI_FROM_DATABASE=Aplex Technology Inc. @@ -67688,6 +68294,9 @@ OUI:70B3D5F25* OUI:70B3D5F27* ID_OUI_FROM_DATABASE=NIRIT- Xinwei Telecom Technology Co., Ltd. +OUI:70B3D5F29* + ID_OUI_FROM_DATABASE=SamabaNova Systems + OUI:70B3D5F2A* ID_OUI_FROM_DATABASE=WIBOND Informationssysteme GmbH @@ -67982,6 +68591,9 @@ OUI:70B3D5FB6* OUI:70B3D5FB7* ID_OUI_FROM_DATABASE=SAICE +OUI:70B3D5FB9* + ID_OUI_FROM_DATABASE=EYEDEA + OUI:70B3D5FBA* ID_OUI_FROM_DATABASE=Apogee Applied Research, Inc. @@ -68051,6 +68663,9 @@ OUI:70B3D5FD3* OUI:70B3D5FD4* ID_OUI_FROM_DATABASE=GETRALINE +OUI:70B3D5FD5* + ID_OUI_FROM_DATABASE=OCEANCCTV LTD + OUI:70B3D5FD6* ID_OUI_FROM_DATABASE=Visual Fan @@ -68186,6 +68801,9 @@ OUI:70C6AC* OUI:70C76F* ID_OUI_FROM_DATABASE=INNO S +OUI:70C7F2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:70C833* ID_OUI_FROM_DATABASE=Wirepas Oy @@ -68384,6 +69002,9 @@ OUI:70F96D* OUI:70FC8C* ID_OUI_FROM_DATABASE=OneAccess SA +OUI:70FC8F* + ID_OUI_FROM_DATABASE=FREEBOX SAS + OUI:70FD46* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -68573,6 +69194,9 @@ OUI:743256* OUI:743400* ID_OUI_FROM_DATABASE=MTG Co., Ltd. +OUI:7434AE* + ID_OUI_FROM_DATABASE=this is engineering Inc. + OUI:74366D* ID_OUI_FROM_DATABASE=Vodafone Italia S.p.A. @@ -68585,6 +69209,9 @@ OUI:74373B* OUI:743889* ID_OUI_FROM_DATABASE=ANNAX Anzeigesysteme GmbH +OUI:7438B7* + ID_OUI_FROM_DATABASE=CANON INC. + OUI:743A65* ID_OUI_FROM_DATABASE=NEC Corporation @@ -68606,6 +69233,9 @@ OUI:744401* OUI:74458A* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:7445CE* + ID_OUI_FROM_DATABASE=CRESYN + OUI:7446A0* ID_OUI_FROM_DATABASE=Hewlett Packard @@ -68639,6 +69269,9 @@ OUI:745612* OUI:745798* ID_OUI_FROM_DATABASE=TRUMPF Laser GmbH + Co. KG +OUI:745909* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:745933* ID_OUI_FROM_DATABASE=Danal Entertainment @@ -69158,6 +69791,9 @@ OUI:74EC42* OUI:74ECF1* ID_OUI_FROM_DATABASE=Acumen +OUI:74EE2A* + ID_OUI_FROM_DATABASE=SHENZHEN BILIAN ELECTRONIC CO.,LTD + OUI:74F06D* ID_OUI_FROM_DATABASE=AzureWave Technology Inc. @@ -69818,6 +70454,9 @@ OUI:78C2C0E* OUI:78C2C0F* ID_OUI_FROM_DATABASE=Private +OUI:78C313* + ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. + OUI:78C3E9* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -69893,6 +70532,9 @@ OUI:78CB33* OUI:78CB68* ID_OUI_FROM_DATABASE=DAEHAP HYPER-TECH +OUI:78CC2B* + ID_OUI_FROM_DATABASE=SINEWY TECHNOLOGY CO., LTD + OUI:78CD8E* ID_OUI_FROM_DATABASE=SMC Networks Inc @@ -69905,6 +70547,9 @@ OUI:78D129* OUI:78D294* ID_OUI_FROM_DATABASE=NETGEAR +OUI:78D347* + ID_OUI_FROM_DATABASE=Ericsson AB + OUI:78D34F* ID_OUI_FROM_DATABASE=Pace-O-Matic, Inc. @@ -70199,6 +70844,9 @@ OUI:7C2048* OUI:7C2064* ID_OUI_FROM_DATABASE=Alcatel-Lucent IPD +OUI:7C2302* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:7C240C* ID_OUI_FROM_DATABASE=Telechips, Inc. @@ -70553,6 +71201,9 @@ OUI:7C8274* OUI:7C8306* ID_OUI_FROM_DATABASE=Glen Dimplex Nordic as +OUI:7C8956* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:7C89C1* ID_OUI_FROM_DATABASE=Palo Alto Networks @@ -70571,6 +71222,9 @@ OUI:7C8EE4* OUI:7C9122* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:7C942A* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:7C94B2* ID_OUI_FROM_DATABASE=Philips Healthcare PCCI @@ -70649,6 +71303,9 @@ OUI:7CB25C* OUI:7CB542* ID_OUI_FROM_DATABASE=ACES Technology +OUI:7CB59B* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:7CB733* ID_OUI_FROM_DATABASE=ASKEY COMPUTER CORP @@ -70931,6 +71588,9 @@ OUI:7CEBEA* OUI:7CEC79* ID_OUI_FROM_DATABASE=Texas Instruments +OUI:7CEC9B* + ID_OUI_FROM_DATABASE=Fuzhou Teraway Information Technology Co.,Ltd + OUI:7CED8D* ID_OUI_FROM_DATABASE=Microsoft @@ -71313,7 +71973,7 @@ OUI:807ABF* ID_OUI_FROM_DATABASE=HTC Corporation OUI:807B1E* - ID_OUI_FROM_DATABASE=Corsair Components + ID_OUI_FROM_DATABASE=Corsair Memory, Inc. OUI:807B850* ID_OUI_FROM_DATABASE=Shiroshita Industrial Co., Ltd. @@ -71411,6 +72071,9 @@ OUI:808F1D* OUI:80912A* ID_OUI_FROM_DATABASE=Lih Rong electronic Enterprise Co., Ltd. +OUI:809133* + ID_OUI_FROM_DATABASE=AzureWave Technology Inc. + OUI:8091C0* ID_OUI_FROM_DATABASE=AgileMesh, Inc. @@ -71726,6 +72389,9 @@ OUI:8404D2* OUI:840B2D* ID_OUI_FROM_DATABASE=SAMSUNG ELECTRO MECHANICS CO., LTD. +OUI:840B7C* + ID_OUI_FROM_DATABASE=Hitron Technologies. Inc + OUI:840D8E* ID_OUI_FROM_DATABASE=Espressif Inc. @@ -72122,6 +72788,45 @@ OUI:8489ECE* OUI:848A8D* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:848BCD0* + ID_OUI_FROM_DATABASE=SouXin Corporate + +OUI:848BCD1* + ID_OUI_FROM_DATABASE=Shenzhen LTIME In-Vehicle Entertainment System Company Limited + +OUI:848BCD2* + ID_OUI_FROM_DATABASE=CCX Technologies Inc. + +OUI:848BCD3* + ID_OUI_FROM_DATABASE=Annapurna labs + +OUI:848BCD4* + ID_OUI_FROM_DATABASE=Logic Supply + +OUI:848BCD5* + ID_OUI_FROM_DATABASE=exodraft a/s + +OUI:848BCD6* + ID_OUI_FROM_DATABASE=TWTG R&D B.V. + +OUI:848BCD7* + ID_OUI_FROM_DATABASE=Smart Code (Shenzhen) Technology Co.,Ltd + +OUI:848BCD9* + ID_OUI_FROM_DATABASE=NORALSY + +OUI:848BCDA* + ID_OUI_FROM_DATABASE=Sphera Telecom + +OUI:848BCDB* + ID_OUI_FROM_DATABASE=CHONGQING HUAYI KANGDAO TECHNOLOGY CO.,LTD. + +OUI:848BCDD* + ID_OUI_FROM_DATABASE=ENGISAT LDA + +OUI:848BCDE* + ID_OUI_FROM_DATABASE=Emotiv Inc + OUI:848D84* ID_OUI_FROM_DATABASE=Rajant Corporation @@ -72548,6 +73253,9 @@ OUI:8828B3* OUI:882950* ID_OUI_FROM_DATABASE=Netmoon Technology Co., Ltd +OUI:88299C* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:882BD7* ID_OUI_FROM_DATABASE=ADDÉNERGIE TECHNOLOGIES @@ -72941,9 +73649,15 @@ OUI:889B39* OUI:889CA6* ID_OUI_FROM_DATABASE=BTB Korea INC +OUI:889E33* + ID_OUI_FROM_DATABASE=TCT mobile ltd + OUI:889F6F* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:889FAA* + ID_OUI_FROM_DATABASE=Hella Gutmann Solutions GmbH + OUI:889FFA* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. @@ -73041,7 +73755,10 @@ OUI:88B291* ID_OUI_FROM_DATABASE=Apple, Inc. OUI:88B362* - ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co. Ltd.) + ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co., Ltd. + +OUI:88B436* + ID_OUI_FROM_DATABASE=Private OUI:88B4A6* ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company @@ -73214,6 +73931,9 @@ OUI:88E9FE* OUI:88ED1C* ID_OUI_FROM_DATABASE=Cudo Communication Co., Ltd. +OUI:88EF16* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:88F031* ID_OUI_FROM_DATABASE=Cisco Systems, Inc @@ -73247,6 +73967,9 @@ OUI:88FED6* OUI:8C006D* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:8C04BA* + ID_OUI_FROM_DATABASE=Dell Inc. + OUI:8C04FF* ID_OUI_FROM_DATABASE=Technicolor CH USA Inc. @@ -73559,6 +74282,33 @@ OUI:8C57FD* OUI:8C5877* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:8C593C1* + ID_OUI_FROM_DATABASE=Future Robot Technology Co., Limited + +OUI:8C593C2* + ID_OUI_FROM_DATABASE=Beida Jade Bird Universal Fire Alarm Device CO.,LTD. + +OUI:8C593C3* + ID_OUI_FROM_DATABASE=Chongqing beimoting technology co.ltd + +OUI:8C593C5* + ID_OUI_FROM_DATABASE=Spectranetix + +OUI:8C593C6* + ID_OUI_FROM_DATABASE=Qbic Technology Co., Ltd + +OUI:8C593C7* + ID_OUI_FROM_DATABASE=OBO Pro.2 Inc. + +OUI:8C593C8* + ID_OUI_FROM_DATABASE=Nanonord A/S + +OUI:8C593C9* + ID_OUI_FROM_DATABASE=GENIS + +OUI:8C593CD* + ID_OUI_FROM_DATABASE=IDRO-ELETTRICA S.P.A. + OUI:8C5973* ID_OUI_FROM_DATABASE=Zyxel Communications Corporation @@ -73766,6 +74516,9 @@ OUI:8CA5A1* OUI:8CA6DF* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. +OUI:8CA96F* + ID_OUI_FROM_DATABASE=D&M Holdings Inc. + OUI:8CA982* ID_OUI_FROM_DATABASE=Intel Corporate @@ -73928,6 +74681,9 @@ OUI:8CE2DA* OUI:8CE38E* ID_OUI_FROM_DATABASE=Toshiba Memory Corporation +OUI:8CE5C0* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:8CE748* ID_OUI_FROM_DATABASE=Private @@ -73979,6 +74735,9 @@ OUI:8CFABA* OUI:8CFCA0* ID_OUI_FROM_DATABASE=Shenzhen Smart Device Technology Co., LTD. +OUI:8CFD18* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:8CFDF0* ID_OUI_FROM_DATABASE=Qualcomm Inc. @@ -74351,9 +75110,15 @@ OUI:907240* OUI:907282* ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS +OUI:90735A* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + OUI:907841* ID_OUI_FROM_DATABASE=Intel Corporate +OUI:9078B2* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:907910* ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. @@ -74759,6 +75524,9 @@ OUI:9405B6* OUI:940937* ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. +OUI:940B19* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:940B2D* ID_OUI_FROM_DATABASE=NetView Technologies(Shenzhen) Co., Ltd @@ -75455,6 +76223,9 @@ OUI:980CA5* OUI:980D2E* ID_OUI_FROM_DATABASE=HTC Corporation +OUI:980D67* + ID_OUI_FROM_DATABASE=Zyxel Communications Corporation + OUI:980EE4* ID_OUI_FROM_DATABASE=Private @@ -75542,6 +76313,9 @@ OUI:983571* OUI:9835B8* ID_OUI_FROM_DATABASE=Assembled Products Corporation +OUI:9835ED* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:983713* ID_OUI_FROM_DATABASE=PT.Navicom Indonesia @@ -75740,6 +76514,9 @@ OUI:988389* OUI:9884E3* ID_OUI_FROM_DATABASE=Texas Instruments +OUI:98865D* + ID_OUI_FROM_DATABASE=Nokia Shanghai Bell Co., Ltd. + OUI:9886B1* ID_OUI_FROM_DATABASE=Flyaudio corporation (China) @@ -76463,6 +77240,9 @@ OUI:9C7A03* OUI:9C7BD2* ID_OUI_FROM_DATABASE=NEOLAB Convergence +OUI:9C7BEF* + ID_OUI_FROM_DATABASE=Hewlett Packard + OUI:9C7DA3* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -76541,6 +77321,9 @@ OUI:9C9811* OUI:9C99A0* ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd +OUI:9C99CD* + ID_OUI_FROM_DATABASE=Voippartners + OUI:9C9C1D* ID_OUI_FROM_DATABASE=Starkey Labs Inc. @@ -77711,6 +78494,9 @@ OUI:A0FE61* OUI:A0FE91* ID_OUI_FROM_DATABASE=AVAT Automation GmbH +OUI:A400E2* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:A40130* ID_OUI_FROM_DATABASE=ABIsystems Co., LTD @@ -78275,6 +79061,9 @@ OUI:A4975C* OUI:A497BB* ID_OUI_FROM_DATABASE=Hitachi Industrial Equipment Systems Co.,Ltd +OUI:A49813* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:A49947* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -78305,6 +79094,9 @@ OUI:A49F85* OUI:A49F89* ID_OUI_FROM_DATABASE=Shanghai Rui Rui Communication Technology Co.Ltd. +OUI:A4A179* + ID_OUI_FROM_DATABASE=Nanjing dianyan electric power automation co. LTD + OUI:A4A1C2* ID_OUI_FROM_DATABASE=Ericsson AB @@ -78329,6 +79121,9 @@ OUI:A4AD00* OUI:A4ADB8* ID_OUI_FROM_DATABASE=Vitec Group, Camera Dynamics Ltd +OUI:A4AE11* + ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co., Ltd. + OUI:A4AE9A* ID_OUI_FROM_DATABASE=Maestro Wireless Solutions ltd. @@ -78818,6 +79613,9 @@ OUI:A845E9* OUI:A8474A* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:A8494D* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:A849A5* ID_OUI_FROM_DATABASE=Lisantech Co., Ltd. @@ -79439,6 +80237,9 @@ OUI:AC40EA* OUI:AC4122* ID_OUI_FROM_DATABASE=Eclipse Electronic Systems Inc. +OUI:AC4228* + ID_OUI_FROM_DATABASE=Parta Networks + OUI:AC4330* ID_OUI_FROM_DATABASE=Versa Networks @@ -79508,6 +80309,9 @@ OUI:AC5AEE* OUI:AC5D10* ID_OUI_FROM_DATABASE=Pace Americas +OUI:AC5D5C* + ID_OUI_FROM_DATABASE=FN-LINK TECHNOLOGY LIMITED + OUI:AC5E8C* ID_OUI_FROM_DATABASE=Utillink @@ -79649,6 +80453,9 @@ OUI:AC81F3* OUI:AC8317* ID_OUI_FROM_DATABASE=Shenzhen Furtunetel Communication Co., Ltd +OUI:AC83E9* + ID_OUI_FROM_DATABASE=Beijing Zile Technology Co., Ltd + OUI:AC83F0* ID_OUI_FROM_DATABASE=ImmediaTV Corporation @@ -79862,6 +80669,9 @@ OUI:ACD657* OUI:ACD9D6* ID_OUI_FROM_DATABASE=tci GmbH +OUI:ACDB48* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:ACDBDA* ID_OUI_FROM_DATABASE=Shenzhen Geniatech Inc, Ltd @@ -79940,6 +80750,9 @@ OUI:ACF1DF* OUI:ACF2C5* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:ACF5E6* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:ACF6F7* ID_OUI_FROM_DATABASE=LG Electronics (Mobile Communications) @@ -80375,6 +81188,9 @@ OUI:B0A2E7* OUI:B0A37E* ID_OUI_FROM_DATABASE=QING DAO HAIER TELECOM CO.,LTD. +OUI:B0A6F5* + ID_OUI_FROM_DATABASE=Xaptum, Inc. + OUI:B0A72A* ID_OUI_FROM_DATABASE=Ensemble Designs, Inc. @@ -80420,6 +81236,9 @@ OUI:B0B3AD* OUI:B0B448* ID_OUI_FROM_DATABASE=Texas Instruments +OUI:B0B5E8* + ID_OUI_FROM_DATABASE=Ruroc LTD + OUI:B0B867* ID_OUI_FROM_DATABASE=Hewlett Packard Enterprise @@ -80660,15 +81479,36 @@ OUI:B0FC0D* OUI:B0FC36* ID_OUI_FROM_DATABASE=CyberTAN Technology Inc. +OUI:B0FD0B0* + ID_OUI_FROM_DATABASE=TAE HYUNG Industrial Electronics Co., Ltd. + +OUI:B0FD0B1* + ID_OUI_FROM_DATABASE=IDspire Corporation Ltd. + OUI:B0FD0B2* ID_OUI_FROM_DATABASE=Vista Manufacturing OUI:B0FD0B3* ID_OUI_FROM_DATABASE=DMAC Security LLC +OUI:B0FD0B4* + ID_OUI_FROM_DATABASE=Fasii Information Technology (Shanghai) Ltd. + +OUI:B0FD0B5* + ID_OUI_FROM_DATABASE=Taian Yuqi Communication Technology Co., Ltd + +OUI:B0FD0B6* + ID_OUI_FROM_DATABASE=DNESO TEN Ltd. + +OUI:B0FD0B7* + ID_OUI_FROM_DATABASE=Everynet Oy + OUI:B0FD0B8* ID_OUI_FROM_DATABASE=eSenseLab Ltd. +OUI:B0FD0B9* + ID_OUI_FROM_DATABASE=Eagle Acoustics Manufacturing, LLC + OUI:B0FD0BA* ID_OUI_FROM_DATABASE=TEMCO JAPAN CO., LTD. @@ -80681,6 +81521,9 @@ OUI:B0FD0BC* OUI:B0FD0BD* ID_OUI_FROM_DATABASE=Habana Labs LTD +OUI:B0FD0BE* + ID_OUI_FROM_DATABASE=Shenzhen FEIBIT Electronic Technology Co.,LTD + OUI:B0FEBD* ID_OUI_FROM_DATABASE=Private @@ -80960,6 +81803,12 @@ OUI:B4527D* OUI:B4527E* ID_OUI_FROM_DATABASE=Sony Mobile Communications Inc +OUI:B452A9* + ID_OUI_FROM_DATABASE=Texas Instruments + +OUI:B45459* + ID_OUI_FROM_DATABASE=China Mobile (Hangzhou) Information Technology Co., Ltd. + OUI:B45570* ID_OUI_FROM_DATABASE=Borea @@ -80975,6 +81824,9 @@ OUI:B45CA4* OUI:B45D50* ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company +OUI:B46077* + ID_OUI_FROM_DATABASE=Sichuan Changhong Electric Ltd. + OUI:B461FF* ID_OUI_FROM_DATABASE=Lumigon A/S @@ -81080,6 +81932,9 @@ OUI:B4994C* OUI:B499BA* ID_OUI_FROM_DATABASE=Hewlett Packard +OUI:B49A95* + ID_OUI_FROM_DATABASE=Shenzhen Boomtech Industrial Corporation + OUI:B49CDF* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -81095,6 +81950,9 @@ OUI:B49EAC* OUI:B49EE6* ID_OUI_FROM_DATABASE=SHENZHEN TECHNOLOGY CO LTD +OUI:B4A305* + ID_OUI_FROM_DATABASE=XIAMEN YAXON NETWORK CO., LTD. + OUI:B4A382* ID_OUI_FROM_DATABASE=Hangzhou Hikvision Digital Technology Co.,Ltd. @@ -81194,6 +82052,9 @@ OUI:B4C170* OUI:B4C44E* ID_OUI_FROM_DATABASE=VXL eTech Pvt Ltd +OUI:B4C4FC* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:B4C62E* ID_OUI_FROM_DATABASE=Molex CMS @@ -81227,6 +82088,9 @@ OUI:B4CEFE* OUI:B4CFDB* ID_OUI_FROM_DATABASE=Shenzhen Jiuzhou Electric Co.,LTD +OUI:B4CFE0* + ID_OUI_FROM_DATABASE=Sichuan tianyi kanghe communications co., LTD + OUI:B4D0A9* ID_OUI_FROM_DATABASE=China Mobile Group Device Co.,Ltd. @@ -81245,6 +82109,9 @@ OUI:B4D8A9* OUI:B4D8DE* ID_OUI_FROM_DATABASE=iota Computing, Inc. +OUI:B4DC09* + ID_OUI_FROM_DATABASE=Guangzhou Dawei Communication Co.,Ltd + OUI:B4DD15* ID_OUI_FROM_DATABASE=ControlThings Oy Ab @@ -81329,6 +82196,9 @@ OUI:B4F2E8* OUI:B4F323* ID_OUI_FROM_DATABASE=PETATEL INC. +OUI:B4F58E* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:B4F61C* ID_OUI_FROM_DATABASE=Apple, Inc. @@ -81368,6 +82238,9 @@ OUI:B805AB* OUI:B80716* ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. +OUI:B80756* + ID_OUI_FROM_DATABASE=Cisco Meraki + OUI:B808CF* ID_OUI_FROM_DATABASE=Intel Corporate @@ -81554,6 +82427,9 @@ OUI:B86491* OUI:B8653B* ID_OUI_FROM_DATABASE=Bolymin, Inc. +OUI:B86685* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:B869C2* ID_OUI_FROM_DATABASE=Sunitec Enterprise Co., Ltd. @@ -81659,6 +82535,9 @@ OUI:B88F14* OUI:B88FB4* ID_OUI_FROM_DATABASE=JABIL CIRCUIT ITALIA S.R.L +OUI:B891C9* + ID_OUI_FROM_DATABASE=Handreamnet + OUI:B8921D* ID_OUI_FROM_DATABASE=BG T&A @@ -81713,6 +82592,9 @@ OUI:B8A386* OUI:B8A3E0* ID_OUI_FROM_DATABASE=BenRui Technology Co.,Ltd +OUI:B8A44F* + ID_OUI_FROM_DATABASE=Axis Communications AB + OUI:B8A8AF* ID_OUI_FROM_DATABASE=Logic S.p.A. @@ -82427,6 +83309,9 @@ OUI:BC79AD* OUI:BC7DD1* ID_OUI_FROM_DATABASE=Radio Data Comms +OUI:BC7FA4* + ID_OUI_FROM_DATABASE=Xiaomi Communications Co Ltd + OUI:BC811F* ID_OUI_FROM_DATABASE=Ingate Systems @@ -82484,9 +83369,60 @@ OUI:BC9325* OUI:BC9680* ID_OUI_FROM_DATABASE=SHENZHEN GONGJIN ELECTRONICS CO.,LT +OUI:BC97400* + ID_OUI_FROM_DATABASE=Alpha ESS Co., Ltd. + +OUI:BC97401* + ID_OUI_FROM_DATABASE=comtac AG + +OUI:BC97402* + ID_OUI_FROM_DATABASE=Lattec I/S + +OUI:BC97403* + ID_OUI_FROM_DATABASE=Precision Galaxy Pvt. Ltd + +OUI:BC97404* + ID_OUI_FROM_DATABASE=Wind Mobility Technology (Beijing) Co., Ltd + +OUI:BC97405* + ID_OUI_FROM_DATABASE=Shanghai Laisi Information Technology Co.,Ltd + +OUI:BC97406* + ID_OUI_FROM_DATABASE=Shenzhen Colorwin Optical Technology Co.,Ltd + +OUI:BC97407* + ID_OUI_FROM_DATABASE=Airfi Oy AB + +OUI:BC97408* + ID_OUI_FROM_DATABASE=Gaodi Rus + +OUI:BC97409* + ID_OUI_FROM_DATABASE=Direct Communication Solutions + +OUI:BC9740A* + ID_OUI_FROM_DATABASE=Amap Information Technology Co., Ltd + +OUI:BC9740B* + ID_OUI_FROM_DATABASE=ForoTel + +OUI:BC9740C* + ID_OUI_FROM_DATABASE=LISTEC GmbH + +OUI:BC9740D* + ID_OUI_FROM_DATABASE=Rollock Oy + +OUI:BC9740E* + ID_OUI_FROM_DATABASE=B4ComTechnologies LLC + +OUI:BC97E1* + ID_OUI_FROM_DATABASE=Broadcom Limited + OUI:BC9889* ID_OUI_FROM_DATABASE=Fiberhome Telecommunication Technologies Co.,LTD +OUI:BC98DF* + ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company + OUI:BC9911* ID_OUI_FROM_DATABASE=Zyxel Communications Corporation @@ -82505,12 +83441,18 @@ OUI:BC9CC5* OUI:BC9DA5* ID_OUI_FROM_DATABASE=DASCOM Europe GmbH +OUI:BC9FE4* + ID_OUI_FROM_DATABASE=Aruba, a Hewlett Packard Enterprise Company + OUI:BC9FEF* ID_OUI_FROM_DATABASE=Apple, Inc. OUI:BCA042* ID_OUI_FROM_DATABASE=SHANGHAI FLYCO ELECTRICAL APPLIANCE CO.,LTD +OUI:BCA13A* + ID_OUI_FROM_DATABASE=SES-imagotag + OUI:BCA4E1* ID_OUI_FROM_DATABASE=Nabto @@ -83024,6 +83966,9 @@ OUI:C0885B* OUI:C08997* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:C089AB* + ID_OUI_FROM_DATABASE=ARRIS Group, Inc. + OUI:C08ACD* ID_OUI_FROM_DATABASE=Guangzhou Shiyuan Electronic Technology Company Limited @@ -83501,6 +84446,9 @@ OUI:C43DC7* OUI:C44044* ID_OUI_FROM_DATABASE=RackTop Systems Inc. +OUI:C4411E* + ID_OUI_FROM_DATABASE=Belkin International Inc. + OUI:C44202* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -83750,12 +84698,18 @@ OUI:C486E9* OUI:C488E5* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd +OUI:C48A5A* + ID_OUI_FROM_DATABASE=JFCONTROL + OUI:C48E8F* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. OUI:C48F07* ID_OUI_FROM_DATABASE=Shenzhen Yihao Hulian Science and Technology Co., Ltd. +OUI:C48FC1* + ID_OUI_FROM_DATABASE=DEEPTRACK S.L.U. + OUI:C4913A* ID_OUI_FROM_DATABASE=Shenzhen Sanland Electronic Co., ltd. @@ -83873,6 +84827,9 @@ OUI:C4C19F* OUI:C4C563* ID_OUI_FROM_DATABASE=TECNO MOBILE LIMITED +OUI:C4C603* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:C4C755* ID_OUI_FROM_DATABASE=Beijing HuaqinWorld Technology Co.,Ltd @@ -83975,6 +84932,9 @@ OUI:C4F57C* OUI:C4F5A5* ID_OUI_FROM_DATABASE=Kumalift Co., Ltd. +OUI:C4F7D5* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:C4F839* ID_OUI_FROM_DATABASE=Actia Automotive @@ -84065,6 +85025,9 @@ OUI:C80AA9* OUI:C80CC8* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:C80D32* + ID_OUI_FROM_DATABASE=Holoplot GmbH + OUI:C80E14* ID_OUI_FROM_DATABASE=AVM Audiovisuelles Marketing und Computersysteme GmbH @@ -84266,6 +85229,9 @@ OUI:C84C75* OUI:C84F86* ID_OUI_FROM_DATABASE=Sophos Ltd +OUI:C850CE* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:C850E9* ID_OUI_FROM_DATABASE=Raisecom Technology CO., LTD @@ -84287,6 +85253,9 @@ OUI:C85663* OUI:C85B76* ID_OUI_FROM_DATABASE=LCFC(HeFei) Electronics Technology co., ltd +OUI:C85D38* + ID_OUI_FROM_DATABASE=HUMAX Co., Ltd. + OUI:C86000* ID_OUI_FROM_DATABASE=ASUSTek COMPUTER INC. @@ -84521,6 +85490,9 @@ OUI:C8A70A* OUI:C8A729* ID_OUI_FROM_DATABASE=SYStronics Co., Ltd. +OUI:C8A776* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:C8A823* ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd @@ -84644,6 +85616,9 @@ OUI:C8D590* OUI:C8D5FE* ID_OUI_FROM_DATABASE=Shenzhen Zowee Technology Co., Ltd +OUI:C8D69D* + ID_OUI_FROM_DATABASE=Arab International Optronics + OUI:C8D719* ID_OUI_FROM_DATABASE=Cisco-Linksys, LLC @@ -84695,6 +85670,9 @@ OUI:C8E7D8* OUI:C8E7F0* ID_OUI_FROM_DATABASE=Juniper Networks +OUI:C8EAF8* + ID_OUI_FROM_DATABASE=zte corporation + OUI:C8EE08* ID_OUI_FROM_DATABASE=TANGTOP TECHNOLOGY CO.,LTD @@ -85115,6 +86093,9 @@ OUI:CC60BB* OUI:CC61E5* ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company +OUI:CC64A6* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:CC65AD* ID_OUI_FROM_DATABASE=ARRIS Group, Inc. @@ -85196,6 +86177,9 @@ OUI:CC82EB* OUI:CC856C* ID_OUI_FROM_DATABASE=SHENZHEN MDK DIGITAL TECHNOLOGY CO.,LTD +OUI:CC8826* + ID_OUI_FROM_DATABASE=LG Innotek + OUI:CC89FD* ID_OUI_FROM_DATABASE=Nokia Corporation @@ -85208,6 +86192,9 @@ OUI:CC8CE3* OUI:CC8E71* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:CC9070* + ID_OUI_FROM_DATABASE=Cisco Systems, Inc + OUI:CC9093* ID_OUI_FROM_DATABASE=Hansong Tehnologies @@ -85727,6 +86714,9 @@ OUI:D03311* OUI:D03742* ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd +OUI:D03745* + ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. + OUI:D03761* ID_OUI_FROM_DATABASE=Texas Instruments @@ -85799,6 +86789,9 @@ OUI:D0577B* OUI:D05785* ID_OUI_FROM_DATABASE=Pantech Co., Ltd. +OUI:D05794* + ID_OUI_FROM_DATABASE=Sagemcom Broadband SAS + OUI:D057A1* ID_OUI_FROM_DATABASE=Werma Signaltechnik GmbH & Co. KG @@ -86144,12 +87137,60 @@ OUI:D0C5D8* OUI:D0C5F3* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:D0C65B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:D0C789* ID_OUI_FROM_DATABASE=Cisco Systems, Inc OUI:D0C7C0* ID_OUI_FROM_DATABASE=TP-LINK TECHNOLOGIES CO.,LTD. +OUI:D0C8570* + ID_OUI_FROM_DATABASE=YUAN High-Tech Development Co., Ltd. + +OUI:D0C8571* + ID_OUI_FROM_DATABASE=DALI A/S + +OUI:D0C8572* + ID_OUI_FROM_DATABASE=FORGAMERS INC. + +OUI:D0C8573* + ID_OUI_FROM_DATABASE=Mobicon + +OUI:D0C8574* + ID_OUI_FROM_DATABASE=Imin Technology Pte Ltd + +OUI:D0C8575* + ID_OUI_FROM_DATABASE=Beijing Inspiry Technology Co., Ltd. + +OUI:D0C8576* + ID_OUI_FROM_DATABASE=Innovative Industrial(HK)Co., Limited + +OUI:D0C8577* + ID_OUI_FROM_DATABASE=Eco Mobile + +OUI:D0C8578* + ID_OUI_FROM_DATABASE=Nanjing Magewell Electronics Co.,Ltd + +OUI:D0C8579* + ID_OUI_FROM_DATABASE=Shenzhen xiaosha Intelligence Technology Co. Ltd + +OUI:D0C857A* + ID_OUI_FROM_DATABASE=shenzhen cnsun + +OUI:D0C857B* + ID_OUI_FROM_DATABASE=CHUNGHSIN INTERNATIONAL ELECTRONICS CO.,LTD. + +OUI:D0C857C* + ID_OUI_FROM_DATABASE=Dante Security Inc. + +OUI:D0C857D* + ID_OUI_FROM_DATABASE=IFLYTEK CO.,LTD. + +OUI:D0C857E* + ID_OUI_FROM_DATABASE=E-T-A Elektrotechnische Apparate GmbH + OUI:D0CDE1* ID_OUI_FROM_DATABASE=Scientech Electronics @@ -86597,6 +87638,9 @@ OUI:D461DA* OUI:D461FE* ID_OUI_FROM_DATABASE=Hangzhou H3C Technologies Co., Limited +OUI:D462EA* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:D463C6* ID_OUI_FROM_DATABASE=Motorola Mobility LLC, a Lenovo Company @@ -87029,6 +88073,9 @@ OUI:D4EE07* OUI:D4F027* ID_OUI_FROM_DATABASE=Navetas Energy Management +OUI:D4F057* + ID_OUI_FROM_DATABASE=Nintendo Co.,Ltd + OUI:D4F0B4* ID_OUI_FROM_DATABASE=Napco Security Technologies @@ -87092,6 +88139,9 @@ OUI:D80DE3* OUI:D80F99* ID_OUI_FROM_DATABASE=Hon Hai Precision Ind. Co.,Ltd. +OUI:D81399* + ID_OUI_FROM_DATABASE=Hui Zhou Gaoshengda Technology Co.,LTD + OUI:D814D6* ID_OUI_FROM_DATABASE=SURE SYSTEM Co Ltd @@ -87608,6 +88658,9 @@ OUI:D8C8E9* OUI:D8C99D* ID_OUI_FROM_DATABASE=EA DISPLAY LIMITED +OUI:D8CA06* + ID_OUI_FROM_DATABASE=Titan DataCenters France + OUI:D8CB8A* ID_OUI_FROM_DATABASE=Micro-Star INTL CO., LTD. @@ -87632,6 +88685,9 @@ OUI:D8D385* OUI:D8D43C* ID_OUI_FROM_DATABASE=Sony Corporation +OUI:D8D4E6* + ID_OUI_FROM_DATABASE=Hytec Inter Co., Ltd. + OUI:D8D5B9* ID_OUI_FROM_DATABASE=Rainforest Automation, Inc. @@ -87687,7 +88743,7 @@ OUI:D8E56D* ID_OUI_FROM_DATABASE=TCT mobile ltd OUI:D8E72B* - ID_OUI_FROM_DATABASE=NetScout Systems, Inc. + ID_OUI_FROM_DATABASE=NetAlly OUI:D8E743* ID_OUI_FROM_DATABASE=Wush, Inc @@ -87710,6 +88766,9 @@ OUI:D8EFCD* OUI:D8F0F2* ID_OUI_FROM_DATABASE=Zeebo Inc +OUI:D8F15B* + ID_OUI_FROM_DATABASE=Espressif Inc. + OUI:D8F1F0* ID_OUI_FROM_DATABASE=Pepxim International Limited @@ -88007,6 +89066,9 @@ OUI:DC5360* OUI:DC537C* ID_OUI_FROM_DATABASE=Compal Broadband Networks, Inc. +OUI:DC54D7* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:DC5583* ID_OUI_FROM_DATABASE=GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD @@ -88148,6 +89210,9 @@ OUI:DCA4CA* OUI:DCA5F4* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:DCA632* + ID_OUI_FROM_DATABASE=Raspberry Pi Trading Ltd + OUI:DCA6BD* ID_OUI_FROM_DATABASE=Beijing Lanbo Technology Co., Ltd. @@ -89156,12 +90221,18 @@ OUI:E4121D* OUI:E41289* ID_OUI_FROM_DATABASE=topsystem Systemhaus GmbH +OUI:E415F6* + ID_OUI_FROM_DATABASE=Texas Instruments + OUI:E417D8* ID_OUI_FROM_DATABASE=8BITDO TECHNOLOGY HK LIMITED OUI:E4186B* ID_OUI_FROM_DATABASE=Zyxel Communications Corporation +OUI:E419C1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:E41A2C* ID_OUI_FROM_DATABASE=ZPE Systems, Inc. @@ -89381,6 +90452,9 @@ OUI:E44F5F* OUI:E4509A* ID_OUI_FROM_DATABASE=HW Communications Ltd +OUI:E454E8* + ID_OUI_FROM_DATABASE=Dell Inc. + OUI:E455EA* ID_OUI_FROM_DATABASE=Dedicated Computing @@ -89633,6 +90707,9 @@ OUI:E4AA5D* OUI:E4AB46* ID_OUI_FROM_DATABASE=UAB Selteka +OUI:E4AB89* + ID_OUI_FROM_DATABASE=MitraStar Technology Corp. + OUI:E4AD7D* ID_OUI_FROM_DATABASE=SCL Elements @@ -89813,6 +90890,9 @@ OUI:E4FB8F* OUI:E4FC82* ID_OUI_FROM_DATABASE=Juniper Networks +OUI:E4FDA1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:E4FED9* ID_OUI_FROM_DATABASE=EDMI Europe Ltd @@ -89963,6 +91043,9 @@ OUI:E820E2* OUI:E82877* ID_OUI_FROM_DATABASE=TMY Co., Ltd. +OUI:E828C1* + ID_OUI_FROM_DATABASE=Eltex Enterprise Ltd. + OUI:E828D5* ID_OUI_FROM_DATABASE=Cots Technology @@ -90665,6 +91748,9 @@ OUI:EC59E7* OUI:EC5A86* ID_OUI_FROM_DATABASE=Yulong Computer Telecommunication Scientific (Shenzhen) Co.,Ltd +OUI:EC5B73* + ID_OUI_FROM_DATABASE=Advanced & Wise Technology Corp. + OUI:EC5C68* ID_OUI_FROM_DATABASE=CHONGQING FUGUI ELECTRONICS CO.,LTD. @@ -90869,6 +91955,9 @@ OUI:ECA86B* OUI:ECA9FA* ID_OUI_FROM_DATABASE=GUANGDONG GENIUS TECHNOLOGY CO., LTD. +OUI:ECAA25* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:ECAAA0* ID_OUI_FROM_DATABASE=PEGATRON CORPORATION @@ -91460,6 +92549,9 @@ OUI:F085C1* OUI:F08A28* ID_OUI_FROM_DATABASE=JIANGSU HENGSION ELECTRONIC S and T CO.,LTD +OUI:F08A76* + ID_OUI_FROM_DATABASE=Samsung Electronics Co.,Ltd + OUI:F08BFE* ID_OUI_FROM_DATABASE=COSTEL.,CO.LTD @@ -91526,6 +92618,9 @@ OUI:F0A225* OUI:F0A764* ID_OUI_FROM_DATABASE=GST Co., Ltd. +OUI:F0A968* + ID_OUI_FROM_DATABASE=Antailiye Technology Co.,Ltd + OUI:F0AB54* ID_OUI_FROM_DATABASE=MITSUMI ELECTRIC CO.,LTD. @@ -91928,6 +93023,9 @@ OUI:F417B8* OUI:F41BA1* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:F41D6B* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + OUI:F41E26* ID_OUI_FROM_DATABASE=Simon-Kaloi Engineering @@ -91976,6 +93074,9 @@ OUI:F431C3* OUI:F4323D* ID_OUI_FROM_DATABASE=Sichuan tianyi kanghe communications co., LTD +OUI:F43328* + ID_OUI_FROM_DATABASE=CIMCON Lighting Inc. + OUI:F436E1* ID_OUI_FROM_DATABASE=Abilis Systems SARL @@ -92654,6 +93755,9 @@ OUI:F82055* OUI:F82285* ID_OUI_FROM_DATABASE=Cypress Technology CO., LTD. +OUI:F82387* + ID_OUI_FROM_DATABASE=Shenzhen Horn Audio Co.,Ltd. + OUI:F823B2* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD @@ -93038,6 +94142,9 @@ OUI:F8A45F* OUI:F8A5C5* ID_OUI_FROM_DATABASE=Cisco Systems, Inc +OUI:F8A763* + ID_OUI_FROM_DATABASE=Zhejiang Tmall Technology Co., Ltd. + OUI:F8A963* ID_OUI_FROM_DATABASE=COMPAL INFORMATION (KUNSHAN) CO., LTD. @@ -93056,6 +94163,9 @@ OUI:F8AB05* OUI:F8AC6D* ID_OUI_FROM_DATABASE=Deltenna Ltd +OUI:F8ADCB* + ID_OUI_FROM_DATABASE=HMD Global Oy + OUI:F8B156* ID_OUI_FROM_DATABASE=Dell Inc. @@ -93254,6 +94364,9 @@ OUI:F8E61A* OUI:F8E71E* ID_OUI_FROM_DATABASE=Ruckus Wireless +OUI:F8E7A0* + ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. + OUI:F8E7B5* ID_OUI_FROM_DATABASE=µTech Tecnologia LTDA @@ -93506,6 +94619,9 @@ OUI:FC4596* OUI:FC48EF* ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD +OUI:FC492D* + ID_OUI_FROM_DATABASE=Amazon Technologies Inc. + OUI:FC4AE9* ID_OUI_FROM_DATABASE=Castlenet Technology Inc. @@ -93761,6 +94877,12 @@ OUI:FCBBA1* OUI:FCBC9C* ID_OUI_FROM_DATABASE=Vimar Spa +OUI:FCBCD1* + ID_OUI_FROM_DATABASE=HUAWEI TECHNOLOGIES CO.,LTD + +OUI:FCBD67* + ID_OUI_FROM_DATABASE=Arista Networks + OUI:FCBE7B* ID_OUI_FROM_DATABASE=vivo Mobile Communication Co., Ltd. @@ -93899,6 +95021,9 @@ OUI:FCE892* OUI:FCE998* ID_OUI_FROM_DATABASE=Apple, Inc. +OUI:FCEA50* + ID_OUI_FROM_DATABASE=Integrated Device Technology (Malaysia) Sdn. Bhd. + OUI:FCECDA* ID_OUI_FROM_DATABASE=Ubiquiti Networks Inc. diff --git a/hwdb/20-acpi-vendor.hwdb b/hwdb/20-acpi-vendor.hwdb index 962820772..bd6c106f8 100644 --- a/hwdb/20-acpi-vendor.hwdb +++ b/hwdb/20-acpi-vendor.hwdb @@ -1,8 +1,8 @@ # This file is part of systemd. # # Data imported from: -# http://www.uefi.org/uefi-pnp-export -# http://www.uefi.org/uefi-acpi-export +# https://uefi.org/uefi-pnp-export +# https://uefi.org/uefi-acpi-export # # With various additions from other sources diff --git a/hwdb/20-acpi-vendor.hwdb.patch b/hwdb/20-acpi-vendor.hwdb.patch index 4a3588189..fdd9efcf3 100644 --- a/hwdb/20-acpi-vendor.hwdb.patch +++ b/hwdb/20-acpi-vendor.hwdb.patch @@ -1,9 +1,9 @@ ---- 20-acpi-vendor.hwdb.base 2019-02-14 10:59:47.388792656 +0100 -+++ 20-acpi-vendor.hwdb 2019-02-14 10:59:47.398792674 +0100 +--- 20-acpi-vendor.hwdb.base 2019-04-08 11:36:50.727757815 +0200 ++++ 20-acpi-vendor.hwdb 2019-04-08 11:36:50.735757868 +0200 @@ -3,6 +3,8 @@ # Data imported from: - # http://www.uefi.org/uefi-pnp-export - # http://www.uefi.org/uefi-acpi-export + # https://uefi.org/uefi-pnp-export + # https://uefi.org/uefi-acpi-export +# +# With various additions from other sources diff --git a/hwdb/20-pci-vendor-model.hwdb b/hwdb/20-pci-vendor-model.hwdb index 9ca2c7bd7..c9423251b 100644 --- a/hwdb/20-pci-vendor-model.hwdb +++ b/hwdb/20-pci-vendor-model.hwdb @@ -579,7 +579,7 @@ pci:v00000F62* ID_VENDOR_FROM_DATABASE=Acrox Technologies Co., Ltd. pci:v00001000* - ID_VENDOR_FROM_DATABASE=LSI Logic / Symbios Logic + ID_VENDOR_FROM_DATABASE=Broadcom / LSI pci:v00001000d00000001* ID_MODEL_FROM_DATABASE=53c810 @@ -716,6 +716,9 @@ pci:v00001000d00000014* pci:v00001000d00000014sv00001028sd00001FD4* ID_MODEL_FROM_DATABASE=MegaRAID Tri-Mode SAS3516 (PERC H745P MX) +pci:v00001000d00000014sv00001137sd0000020E* + ID_MODEL_FROM_DATABASE=MegaRAID Tri-Mode SAS3516 (UCSC-RAID-M5 12G Modular RAID Controller) + pci:v00001000d00000014sv00001D49sd00000602* ID_MODEL_FROM_DATABASE=MegaRAID Tri-Mode SAS3516 (ThinkSystem RAID 930-16i 4GB Flash PCIe 12Gb Adapter) @@ -1116,7 +1119,7 @@ pci:v00001000d0000005Bsv00008086sd00003512* ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (RMT3PB080 RAID Controller) pci:v00001000d0000005Bsv00008086sd00003513* - ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (RMS25CB080 RAID Controller) + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (Integrated RAID Module RMS25CB080) pci:v00001000d0000005Bsv00008086sd00003514* ID_MODEL_FROM_DATABASE=MegaRAID SAS 2208 [Thunderbolt] (RMS25CB040 RAID Controller) @@ -1352,9 +1355,15 @@ pci:v00001000d00000062sv00001000sd00000062* pci:v00001000d00000064* ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] +pci:v00001000d00000064sv00001000sd00003030* + ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] (9200-16e 6Gb/s SAS/SATA PCIe x8 External HBA) + pci:v00001000d00000064sv00001000sd000030C0* ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] (SAS 9201-16i) +pci:v00001000d00000064sv00001000sd000030D0* + ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] (9201-16e 6Gb/s SAS/SATA PCIe x8 External HBA) + pci:v00001000d00000065* ID_MODEL_FROM_DATABASE=SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] @@ -1421,6 +1430,9 @@ pci:v00001000d00000073sv00001000sd000092A0* pci:v00001000d00000073sv00001014sd000003B1* ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (ServeRAID M1015 SAS/SATA Controller) +pci:v00001000d00000073sv00001014sd0000040D* + ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (ServeRAID M1115 SAS/SATA Controller) + pci:v00001000d00000073sv00001028sd00001F4E* ID_MODEL_FROM_DATABASE=MegaRAID SAS 2008 [Falcon] (PERC H310 Adapter) @@ -2372,9 +2384,6 @@ pci:v00001002d00001714* pci:v00001002d00001714sv0000103Csd0000168B* ID_MODEL_FROM_DATABASE=BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series] (ProBook 4535s) -pci:v00001002d00002191* - ID_MODEL_FROM_DATABASE=TU116M - pci:v00001002d00003150* ID_MODEL_FROM_DATABASE=RV380/M24 [Mobility Radeon X600] @@ -7232,6 +7241,9 @@ pci:v00001002d000067DFsv00001462sd00003416* pci:v00001002d000067DFsv00001462sd00003418* ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580 Armor 4G OC) +pci:v00001002d000067DFsv00001462sd00008A92* + ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580) + pci:v00001002d000067DFsv0000148Csd00002372* ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 480) @@ -7247,6 +7259,9 @@ pci:v00001002d000067DFsv00001682sd00009480* pci:v00001002d000067DFsv00001682sd00009588* ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 580 XTR) +pci:v00001002d000067DFsv00001682sd0000C570* + ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 570) + pci:v00001002d000067DFsv0000174Bsd0000E347* ID_MODEL_FROM_DATABASE=Ellesmere [Radeon RX 470/480/570/570X/580/580X/590] (Radeon RX 470/480) @@ -7319,6 +7334,9 @@ pci:v00001002d000067EB* pci:v00001002d000067EF* ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] +pci:v00001002d000067EFsv0000103Csd00003421* + ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon RX 460) + pci:v00001002d000067EFsv0000106Bsd00000160* ID_MODEL_FROM_DATABASE=Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] (Radeon Pro 460) @@ -8033,6 +8051,12 @@ pci:v00001002d0000686C* pci:v00001002d0000687F* ID_MODEL_FROM_DATABASE=Vega 10 XL/XT [Radeon RX Vega 56/64] +pci:v00001002d0000687Fsv00001002sd00000B36* + ID_MODEL_FROM_DATABASE=Vega 10 XL/XT [Radeon RX Vega 56/64] (RX Vega64) + +pci:v00001002d0000687Fsv00001002sd00006B76* + ID_MODEL_FROM_DATABASE=Vega 10 XL/XT [Radeon RX Vega 56/64] (RX Vega56) + pci:v00001002d00006880* ID_MODEL_FROM_DATABASE=Lexington [Radeon HD 6550M] @@ -10683,34 +10707,34 @@ pci:v00001002d00009616* ID_MODEL_FROM_DATABASE=RS780L [Radeon 3000] pci:v00001002d00009640* - ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6550D] + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6550D] pci:v00001002d00009641* - ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6620G] + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6620G] pci:v00001002d00009642* - ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6370D] + ID_MODEL_FROM_DATABASE=SuperSumo [Radeon HD 6370D] pci:v00001002d00009643* - ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6380G] + ID_MODEL_FROM_DATABASE=SuperSumo [Radeon HD 6380G] pci:v00001002d00009644* - ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6410D] + ID_MODEL_FROM_DATABASE=SuperSumo [Radeon HD 6410D] pci:v00001002d00009645* - ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6410D] + ID_MODEL_FROM_DATABASE=SuperSumo [Radeon HD 6410D] pci:v00001002d00009647* - ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6520G] + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6520G] pci:v00001002d00009648* ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6480G] pci:v00001002d00009649* - ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6480G] + ID_MODEL_FROM_DATABASE=SuperSumo [Radeon HD 6480G] pci:v00001002d0000964A* - ID_MODEL_FROM_DATABASE=BeaverCreek [Radeon HD 6530D] + ID_MODEL_FROM_DATABASE=Sumo [Radeon HD 6530D] pci:v00001002d0000964B* ID_MODEL_FROM_DATABASE=Sumo @@ -11162,6 +11186,9 @@ pci:v00001002d0000AAA0* pci:v00001002d0000AAB0* ID_MODEL_FROM_DATABASE=Oland/Hainan/Cape Verde/Pitcairn HDMI Audio [Radeon HD 7000 Series] +pci:v00001002d0000AAB8* + ID_MODEL_FROM_DATABASE=Tiran HDMI Audio + pci:v00001002d0000AAC0* ID_MODEL_FROM_DATABASE=Tobago HDMI Audio [Radeon R7 360 / R9 360 OEM] @@ -12978,7 +13005,7 @@ pci:v00001022d00001437* ID_MODEL_FROM_DATABASE=Liverpool I/O Memory Management Unit pci:v00001022d00001438* - ID_MODEL_FROM_DATABASE=Liverpool Processor Root Port + ID_MODEL_FROM_DATABASE=Liverpool UMI PCIe Dummy Host Bridge pci:v00001022d00001439* ID_MODEL_FROM_DATABASE=Family 16h Processor Functions 5:1 @@ -13077,7 +13104,7 @@ pci:v00001022d0000145E* ID_MODEL_FROM_DATABASE=Zeppelin Switch Downstream (PCIE SW.DS) pci:v00001022d0000145F* - ID_MODEL_FROM_DATABASE=USB 3.0 Host controller + ID_MODEL_FROM_DATABASE=Zeppelin USB 3.0 Host controller pci:v00001022d00001460* ID_MODEL_FROM_DATABASE=Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 0 @@ -19706,6 +19733,9 @@ pci:v0000106Bd00001801* pci:v0000106Bd00001802* ID_MODEL_FROM_DATABASE=T2 Secure Enclave Processor +pci:v0000106Bd00001803* + ID_MODEL_FROM_DATABASE=Apple Audio Device + pci:v0000106Bd00002001* ID_MODEL_FROM_DATABASE=S1X NVMe Controller @@ -19931,6 +19961,9 @@ pci:v00001077d00001656sv00001077sd0000E4F6* pci:v00001077d00001656sv00001077sd0000E4F7* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 25GbE Controller (FastLinQ QL45212H 25GbE Adapter) +pci:v00001077d00001656sv00001590sd00000245* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 25GbE Controller (10/20/25GbE 2P 4820c CNA) + pci:v00001077d0000165C* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 10/25/40/50GbE Controller (FCoE) @@ -19943,6 +19976,9 @@ pci:v00001077d0000165Csv00001077sd0000E4F1* pci:v00001077d0000165Csv00001077sd0000E4F2* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 10/25/40/50GbE Controller (FCoE) (FastLinQ QL45461H 40GbE FCoE Adapter) +pci:v00001077d0000165Csv00001590sd00000245* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 10/25/40/50GbE Controller (FCoE) (10/20/25GbE 2P 4820c CNA FCoE) + pci:v00001077d0000165E* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 10/25/40/50GbE Controller (iSCSI) @@ -19955,6 +19991,9 @@ pci:v00001077d0000165Esv00001077sd0000E4F1* pci:v00001077d0000165Esv00001077sd0000E4F2* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 10/25/40/50GbE Controller (iSCSI) (FastLinQ QL45461H 40GbE iSCSI Adapter) +pci:v00001077d0000165Esv00001590sd00000245* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series 10/25/40/50GbE Controller (iSCSI) (10/20/25GbE 2P 4820c CNA iSCSI) + pci:v00001077d00001664* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) @@ -19979,6 +20018,9 @@ pci:v00001077d00001664sv00001077sd0000E4F7* pci:v00001077d00001664sv00001077sd0000E4F8* ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (FastLinQ QL45611H 100GbE Adapter (SR-IOV VF)) +pci:v00001077d00001664sv00001590sd00000245* + ID_MODEL_FROM_DATABASE=FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) (10/20/25GbE 2P 4820c CNA SRIOV) + pci:v00001077d00002020* ID_MODEL_FROM_DATABASE=ISP2020A Fast!SCSI Basic Adapter @@ -20012,6 +20054,15 @@ pci:v00001077d00002071sv00001077sd000002A2* pci:v00001077d00002071sv00001077sd000002AD* ID_MODEL_FROM_DATABASE=ISP2714-based 16/32Gb Fibre Channel to PCIe Adapter (QLE2694U Quad Port 16/32Gb Fibre Channel to PCIe Adapter) +pci:v00001077d00002081* + ID_MODEL_FROM_DATABASE=ISP2814-based 64/32G Fibre Channel to PCIe Controller + +pci:v00001077d00002081sv00001077sd000002E1* + ID_MODEL_FROM_DATABASE=ISP2814-based 64/32G Fibre Channel to PCIe Controller (QLE2874 Quad Port 64GFC PCIe Gen4 x16 Adapter) + +pci:v00001077d00002081sv00001077sd000002E3* + ID_MODEL_FROM_DATABASE=ISP2814-based 64/32G Fibre Channel to PCIe Controller (QLE2774 Quad Port 32GFC PCIe Gen4 x16 Adapter) + pci:v00001077d00002100* ID_MODEL_FROM_DATABASE=QLA2100 64-bit Fibre Channel Adapter @@ -20069,6 +20120,21 @@ pci:v00001077d00002261sv00001590sd00000203* pci:v00001077d00002261sv00001590sd00000204* ID_MODEL_FROM_DATABASE=ISP2722-based 16/32Gb Fibre Channel to PCIe Adapter (StoreFabric SN1600Q 32Gb Dual Port Fibre Channel Host Bus Adapter) +pci:v00001077d00002281* + ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller + +pci:v00001077d00002281sv00001077sd000002E2* + ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLE2872 Dual Port 64GFC PCIe Gen4 x8 Adapter) + +pci:v00001077d00002281sv00001077sd000002E4* + ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLE2772 Dual Port 32GFC PCIe Gen4 x8 Adapter) + +pci:v00001077d00002281sv00001077sd000002EE* + ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLE2870 Single Port 64GFC PCIe Gen4 x8 Adapter) + +pci:v00001077d00002281sv00001077sd000002F0* + ID_MODEL_FROM_DATABASE=ISP2812-based 64/32G Fibre Channel to PCIe Controller (QLE2770 Single Port 32GFC PCIe Gen4 x8 Adapter) + pci:v00001077d00002300* ID_MODEL_FROM_DATABASE=QLA2300 64-bit Fibre Channel Adapter @@ -27362,6 +27428,9 @@ pci:v000010DEd00000112* pci:v000010DEd00000113* ID_MODEL_FROM_DATABASE=NV11GL [Quadro2 MXR/EX/Go] +pci:v000010DEd00000113sv00001028sd000000E5* + ID_MODEL_FROM_DATABASE=NV11GL [Quadro2 MXR/EX/Go] (Quadro2 Go) + pci:v000010DEd00000140* ID_MODEL_FROM_DATABASE=NV43 [GeForce 6600 GT] @@ -30083,6 +30152,9 @@ pci:v000010DEd000006CB* pci:v000010DEd000006CD* ID_MODEL_FROM_DATABASE=GF100 [GeForce GTX 470] +pci:v000010DEd000006D0* + ID_MODEL_FROM_DATABASE=GF100GL + pci:v000010DEd000006D1* ID_MODEL_FROM_DATABASE=GF100GL [Tesla C2050 / C2070] @@ -32133,7 +32205,7 @@ pci:v000010DEd00001023sv000010DEsd0000097E* ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40m] (12GB Computational Accelerator) pci:v000010DEd00001024* - ID_MODEL_FROM_DATABASE=GK110BGL [Tesla K40c] + ID_MODEL_FROM_DATABASE=GK180GL [Tesla K40c] pci:v000010DEd00001026* ID_MODEL_FROM_DATABASE=GK110GL [Tesla K20s] @@ -34665,7 +34737,7 @@ pci:v000010DEd00001EB8* ID_MODEL_FROM_DATABASE=TU104GL [Tesla T4] pci:v000010DEd00001ED0* - ID_MODEL_FROM_DATABASE=TU104M [GeForce RTX 2080 Mobile] + ID_MODEL_FROM_DATABASE=TU104BM [GeForce RTX 2080 Mobile] pci:v000010DEd00001F02* ID_MODEL_FROM_DATABASE=TU106 [GeForce RTX 2070] @@ -34692,22 +34764,22 @@ pci:v000010DEd00001F2E* ID_MODEL_FROM_DATABASE=TU106M pci:v000010DEd00001F50* - ID_MODEL_FROM_DATABASE=TU106M [GeForce RTX 2070 Mobile] + ID_MODEL_FROM_DATABASE=TU106BM [GeForce RTX 2070 Mobile] pci:v000010DEd00001F51* - ID_MODEL_FROM_DATABASE=TU106M [GeForce RTX 2060 Mobile] + ID_MODEL_FROM_DATABASE=TU106BM [GeForce RTX 2060 Mobile] pci:v000010DEd00001F82* - ID_MODEL_FROM_DATABASE=TU107 + ID_MODEL_FROM_DATABASE=TU107 [GeForce GTX 1650] pci:v000010DEd00001F92* - ID_MODEL_FROM_DATABASE=TU107M + ID_MODEL_FROM_DATABASE=TU107M [GeForce GTX 1650 Mobile] pci:v000010DEd00001FBF* ID_MODEL_FROM_DATABASE=TU107GL pci:v000010DEd00002182* - ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660 Ti Rev. A] + ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660 Ti] pci:v000010DEd00002183* ID_MODEL_FROM_DATABASE=TU116 @@ -34716,7 +34788,7 @@ pci:v000010DEd00002184* ID_MODEL_FROM_DATABASE=TU116 [GeForce GTX 1660] pci:v000010DEd00002191* - ID_MODEL_FROM_DATABASE=TU116M + ID_MODEL_FROM_DATABASE=TU116M [GeForce GTX 1660 Mobile] pci:v000010DEd000021AE* ID_MODEL_FROM_DATABASE=TU116GL @@ -34724,6 +34796,9 @@ pci:v000010DEd000021AE* pci:v000010DEd000021BF* ID_MODEL_FROM_DATABASE=TU116GL +pci:v000010DEd000021D1* + ID_MODEL_FROM_DATABASE=TU116BM [GeForce GTX 1660 Mobile] + pci:v000010DF* ID_VENDOR_FROM_DATABASE=Emulex Corporation @@ -35264,6 +35339,9 @@ pci:v000010ECd00005229sv000017AAsd00003832* pci:v000010ECd0000522A* ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader +pci:v000010ECd0000522Asv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=RTS522A PCI Express Card Reader (EliteBook 840 G3) + pci:v000010ECd00005249* ID_MODEL_FROM_DATABASE=RTS5249 PCI Express Card Reader @@ -35288,6 +35366,9 @@ pci:v000010ECd0000525Asv00001028sd000006E4* pci:v000010ECd0000525Asv000017AAsd0000224F* ID_MODEL_FROM_DATABASE=RTS525A PCI Express Card Reader (ThinkPad X1 Carbon 5th Gen) +pci:v000010ECd00005260* + ID_MODEL_FROM_DATABASE=RTS5260 PCI Express Card Reader + pci:v000010ECd00005286* ID_MODEL_FROM_DATABASE=RTS5286 PCI Express Card Reader @@ -39107,6 +39188,9 @@ pci:v0000112Fd00000000* pci:v0000112Fd00000001* ID_MODEL_FROM_DATABASE=MVC IM-PCI Video frame grabber/processor +pci:v0000112Fd00000004* + ID_MODEL_FROM_DATABASE=PCDig Digital Image Capture + pci:v0000112Fd00000008* ID_MODEL_FROM_DATABASE=PC-CamLink PCI framegrabber @@ -43214,6 +43298,9 @@ pci:v000011ABd00007823* pci:v000011ABd00007846* ID_MODEL_FROM_DATABASE=88F6820 [Armada 385] ARM SoC +pci:v000011ABd0000D40F* + ID_MODEL_FROM_DATABASE=Bobcat3 Ethernet Switch + pci:v000011ABd0000F003* ID_MODEL_FROM_DATABASE=GT-64010 Primary Image Piranha Image Generator @@ -54683,6 +54770,9 @@ pci:v000014E4d000016D8* pci:v000014E4d000016D8sv00001028sd00001FEB* ID_MODEL_FROM_DATABASE=BCM57416 NetXtreme-E Dual-Media 10G RDMA Ethernet Controller (NetXtreme-E 10Gb SFP+ Adapter) +pci:v000014E4d000016D8sv000014E4sd00004163* + ID_MODEL_FROM_DATABASE=BCM57416 NetXtreme-E Dual-Media 10G RDMA Ethernet Controller (BCM957416M4163C OCP 2x10GBT Type1 wRoCE) + pci:v000014E4d000016D8sv00001590sd0000020C* ID_MODEL_FROM_DATABASE=BCM57416 NetXtreme-E Dual-Media 10G RDMA Ethernet Controller (Ethernet 10Gb 2-port 535T Adapter) @@ -57593,12 +57683,24 @@ pci:v000015B3d00000212* pci:v000015B3d00000213* ID_MODEL_FROM_DATABASE=MT2892 Family [ConnectX-6 Dx Secure Flash Recovery] +pci:v000015B3d00000214* + ID_MODEL_FROM_DATABASE=MT42822 Family [BlueField-2 SoC Flash Recovery] + +pci:v000015B3d00000215* + ID_MODEL_FROM_DATABASE=MT42822 Family [BlueField-2 Secure Flash Recovery] + pci:v000015B3d0000024E* ID_MODEL_FROM_DATABASE=MT53100 [Spectrum-2, Flash recovery mode] pci:v000015B3d0000024F* ID_MODEL_FROM_DATABASE=MT53100 [Spectrum-2, Secure Flash recovery mode] +pci:v000015B3d00000250* + ID_MODEL_FROM_DATABASE=Spectrum-3, Flash recovery mode + +pci:v000015B3d00000251* + ID_MODEL_FROM_DATABASE=Spectrum-3, Secure Flash recovery mode + pci:v000015B3d00000262* ID_MODEL_FROM_DATABASE=MT27710 [ConnectX-4 Lx Programmable] EN @@ -57834,7 +57936,7 @@ pci:v000015B3d0000101D* ID_MODEL_FROM_DATABASE=MT2892 Family [ConnectX-6 Dx] pci:v000015B3d0000101E* - ID_MODEL_FROM_DATABASE=MT2892 Family [ConnectX-6 Dx Virtual Function] + ID_MODEL_FROM_DATABASE=ConnectX Family mlx5Gen Virtual Function pci:v000015B3d0000101F* ID_MODEL_FROM_DATABASE=MT28851 @@ -57851,6 +57953,9 @@ pci:v000015B3d00001974* pci:v000015B3d00001975* ID_MODEL_FROM_DATABASE=MT416842 Family [BlueField SoC PCIe Bridge] +pci:v000015B3d00001978* + ID_MODEL_FROM_DATABASE=MT42822 Family [BlueField-2 SoC PCIe Bridge] + pci:v000015B3d00004117* ID_MODEL_FROM_DATABASE=MT27712A0-FDCF-AE @@ -57878,6 +57983,9 @@ pci:v000015B3d00005E8C* pci:v000015B3d00005E8D* ID_MODEL_FROM_DATABASE=MT25204 [InfiniHost III Lx HCA Flash Recovery] +pci:v000015B3d00006001* + ID_MODEL_FROM_DATABASE=NVMe SNAP Controller + pci:v000015B3d00006274* ID_MODEL_FROM_DATABASE=MT25204 [InfiniHost III Lx HCA] @@ -57995,6 +58103,21 @@ pci:v000015B3d0000A2D2* pci:v000015B3d0000A2D3* ID_MODEL_FROM_DATABASE=MT416842 BlueField multicore SoC family VF +pci:v000015B3d0000A2D4* + ID_MODEL_FROM_DATABASE=MT42822 BlueField-2 SoC Crypto enabled + +pci:v000015B3d0000A2D5* + ID_MODEL_FROM_DATABASE=MT42822 BlueField-2 SoC Crypto disabled + +pci:v000015B3d0000A2D6* + ID_MODEL_FROM_DATABASE=MT42822 BlueField-2 integrated ConnectX-6 Dx network controller + +pci:v000015B3d0000C2D2* + ID_MODEL_FROM_DATABASE=MT416842 BlueField SoC management interfac + +pci:v000015B3d0000C2D3* + ID_MODEL_FROM_DATABASE=MT42822 BlueField-2 SoC Management Interface + pci:v000015B3d0000C738* ID_MODEL_FROM_DATABASE=MT51136 @@ -58019,6 +58142,9 @@ pci:v000015B3d0000CF08* pci:v000015B3d0000CF6C* ID_MODEL_FROM_DATABASE=MT53100 [Spectrum-2] +pci:v000015B3d0000CF70* + ID_MODEL_FROM_DATABASE=Spectrum-3 + pci:v000015B3d0000D2F0* ID_MODEL_FROM_DATABASE=Quantum HDR (200Gbps) switch @@ -61235,6 +61361,12 @@ pci:v000017DFd00001916* pci:v000017DFd00001917* ID_MODEL_FROM_DATABASE=UltrascalePlus PCIe Accelerator Board [DNPCIe_400G_VU_LL] +pci:v000017DFd00001918* + ID_MODEL_FROM_DATABASE=VirtexUS+ ASIC Emulation Board [DNVUPF4A] + +pci:v000017DFd00001919* + ID_MODEL_FROM_DATABASE=UltrascalePlus PCIe Accelerator Board [DNPCIe_400G_VUP_HBM_LL] + pci:v000017DFd00001A00* ID_MODEL_FROM_DATABASE=Virtex6 PCIe DMA Netlist Design @@ -61271,6 +61403,12 @@ pci:v000017DFd00001A0B* pci:v000017DFd00001A0C* ID_MODEL_FROM_DATABASE=KintexUS PCIe DRAM Packet Capture Design [DNPCIe_40G_KU_LL] +pci:v000017DFd00001A0D* + ID_MODEL_FROM_DATABASE=KintexUS PCIe DRAM Packet Capture Design [DNPCIe_40G_KU_LL_2QSFP] + +pci:v000017DFd00001A0E* + ID_MODEL_FROM_DATABASE=UltrascalePlus PCIe Darklite Design [DNPCIe_400G_VUP_HBM_LL] + pci:v000017E4* ID_VENDOR_FROM_DATABASE=Sectra AB @@ -62216,6 +62354,9 @@ pci:v000018F4d00000185* pci:v000018F4d000001A5* ID_MODEL_FROM_DATABASE=NT200A01 Network Adapter +pci:v000018F4d000001C5* + ID_MODEL_FROM_DATABASE=NT200A02 Network Adapter + pci:v000018F6* ID_VENDOR_FROM_DATABASE=NextIO @@ -62696,9 +62837,27 @@ pci:v00001924d00000B03sv00001924sd0000801E* pci:v00001924d00000B03sv00001924sd00008022* ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2522 10G Network Adapter) +pci:v00001924d00000B03sv00001924sd00008024* + ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2562 OCP 3.0 Dual Port SFP28) + +pci:v00001924d00000B03sv00001924sd00008027* + ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2541 PCIe Single Port QSFP28) + pci:v00001924d00000B03sv00001924sd00008028* ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2522-25G Network Adapter) +pci:v00001924d00000B03sv00001924sd0000802A* + ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2542 PCIe Dual Port QSFP28) + +pci:v00001924d00000B03sv00001924sd0000802B* + ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2552 OCP 2.0 Dual Port SFP28) + +pci:v00001924d00000B03sv00001924sd0000802C* + ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2522-25G PCIe Dual Port SFP28) + +pci:v00001924d00000B03sv00001924sd0000802D* + ID_MODEL_FROM_DATABASE=XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller (XtremeScale X2562 OCP 3.0 Dual Port SFP28) + pci:v00001924d00001803* ID_MODEL_FROM_DATABASE=SFC9020 10G Ethernet Controller (Virtual Function) @@ -64637,6 +64796,9 @@ pci:v00001B4Bd00009230sv00001028sd00001FDF* pci:v00001B4Bd00009230sv00001028sd00001FE2* ID_MODEL_FROM_DATABASE=88SE9230 PCIe SATA 6Gb/s Controller (BOSS-S1 Adapter) +pci:v00001B4Bd00009230sv00001028sd00002010* + ID_MODEL_FROM_DATABASE=88SE9230 PCIe SATA 6Gb/s Controller (BOSS-S2 Adapter) + pci:v00001B4Bd00009230sv00001D49sd00000300* ID_MODEL_FROM_DATABASE=88SE9230 PCIe SATA 6Gb/s Controller (ThinkSystem M.2 with Mirroring Enablement Kit) @@ -64922,6 +65084,9 @@ pci:v00001BF4* pci:v00001BF4d00000001* ID_MODEL_FROM_DATABASE=SentinelEX +pci:v00001BF4d00007011* + ID_MODEL_FROM_DATABASE=RX0xxx + pci:v00001BFD* ID_VENDOR_FROM_DATABASE=EeeTOP @@ -64977,7 +65142,7 @@ pci:v00001C28d00000122* ID_MODEL_FROM_DATABASE=M6e PCI Express SSD [Marvell 88SS9183] pci:v00001C2C* - ID_VENDOR_FROM_DATABASE=Fiberblaze + ID_VENDOR_FROM_DATABASE=Silicom Denmark pci:v00001C2Cd0000000A* ID_MODEL_FROM_DATABASE=Capture @@ -65010,13 +65175,31 @@ pci:v00001C2Cd000000A9* ID_MODEL_FROM_DATABASE=FBC2XGHH Capture 2x10Gb pci:v00001C2Cd000000AD* - ID_MODEL_FROM_DATABASE=FBC2CGG3HL Capture 2x200Gb + ID_MODEL_FROM_DATABASE=FBC2CGG3HL Capture 2x100Gb [Padua] pci:v00001C2Cd000000AF* ID_MODEL_FROM_DATABASE=Capture slave device +pci:v00001C2Cd000000E0* + ID_MODEL_FROM_DATABASE=PacketMover 2x100Gb [Savona] + +pci:v00001C2Cd000000E1* + ID_MODEL_FROM_DATABASE=PacketMover 2x100Gb [Tivoli] + pci:v00001C2Cd0000A001* - ID_MODEL_FROM_DATABASE=FBC2CGG3 Capture 2x200Gb + ID_MODEL_FROM_DATABASE=FBC2CGG3 Capture 2x100Gb [Mango] + +pci:v00001C2Cd0000A00E* + ID_MODEL_FROM_DATABASE=FB2CG Capture 2x100Gb [Savona] + +pci:v00001C2Cd0000A00F* + ID_MODEL_FROM_DATABASE=FB2CG Capture 2x40Gb [Savona] + +pci:v00001C2Cd0000A011* + ID_MODEL_FROM_DATABASE=FB2CG Capture 2x25Gb [Savona] + +pci:v00001C2Cd0000A012* + ID_MODEL_FROM_DATABASE=FB2CG Capture 8x10Gb [Savona] pci:v00001C32* ID_VENDOR_FROM_DATABASE=Highland Technology, Inc. @@ -65120,6 +65303,12 @@ pci:v00001CB5d00000002* pci:v00001CB8* ID_VENDOR_FROM_DATABASE=Dawning Information Industry Co., Ltd. +pci:v00001CC4* + ID_VENDOR_FROM_DATABASE=Union Memory (Shenzhen) + +pci:v00001CC4d000017AB* + ID_MODEL_FROM_DATABASE=NVMe 256G SSD device + pci:v00001CC5* ID_VENDOR_FROM_DATABASE=Embedded Intelligence, Inc. @@ -65573,6 +65762,9 @@ pci:v00001D87d00001808* pci:v00001D8F* ID_VENDOR_FROM_DATABASE=Enyx +pci:v00001D93* + ID_VENDOR_FROM_DATABASE=YADRO (KNS Group) + pci:v00001D94* ID_VENDOR_FROM_DATABASE=Chengdu Haiguang IC Design Co., Ltd. @@ -65672,12 +65864,27 @@ pci:v00001D95d00000001* pci:v00001D95d00000002* ID_MODEL_FROM_DATABASE=Colossus GC1 [S1] +pci:v00001D9B* + ID_VENDOR_FROM_DATABASE=Facebook, Inc. + +pci:v00001D9Bd00000010* + ID_MODEL_FROM_DATABASE=Networking DOM Engine + +pci:v00001D9Bd00000011* + ID_MODEL_FROM_DATABASE=IO Bridge + pci:v00001DA1* ID_VENDOR_FROM_DATABASE=Teko Telecom S.r.l. pci:v00001DA2* ID_VENDOR_FROM_DATABASE=Sapphire Technology Limited +pci:v00001DA3* + ID_VENDOR_FROM_DATABASE=Habana Labs Ltd. + +pci:v00001DA3d00000001* + ID_MODEL_FROM_DATABASE=HL-1000 AI Inference Accelerator [Goya] + pci:v00001DBB* ID_VENDOR_FROM_DATABASE=NGD Systems, Inc. @@ -65765,6 +65972,9 @@ pci:v00001DF3d00000202sv00001DF3sd00000002* pci:v00001DF3d00000203* ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator +pci:v00001DF3d00000203sv00001DF3sd00000000* + ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator (Maintenance Mode) + pci:v00001DF3d00000203sv00001DF3sd00000001* ID_MODEL_FROM_DATABASE=ACE-NIC100 Programmable Network Accelerator (ENA2080F) @@ -65816,6 +66026,9 @@ pci:v00001E24d0000021F* pci:v00001E24d00001525* ID_MODEL_FROM_DATABASE=Xilinx BCU-1525 +pci:v00001E26* + ID_VENDOR_FROM_DATABASE=Fujitsu Client Computing Limited + pci:v00001E38* ID_VENDOR_FROM_DATABASE=Thinci, Inc @@ -69566,6 +69779,9 @@ pci:v00008086d00000A54sv0000108Esd00004870* 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:v00008086d00000A54sv0000108Esd00004879* + ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [3DNAND, Beta Rock Controller] (NVMe PCIe 3.0 SSD v2 6.4TB AIC (P4618)) + 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)) @@ -69902,6 +70118,12 @@ pci:v00008086d00000D26* pci:v00008086d00000D36* ID_MODEL_FROM_DATABASE=Crystal Well Integrated Graphics Controller +pci:v00008086d00000D4E* + ID_MODEL_FROM_DATABASE=Ethernet Connection (10) I219-LM + +pci:v00008086d00000D4F* + ID_MODEL_FROM_DATABASE=Ethernet Connection (10) I219-V + pci:v00008086d00000D58* ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 Intel(R) FPGA Programmable Acceleration Card N3000 for Networking @@ -71942,6 +72164,9 @@ pci:v00008086d000010FBsv0000108Esd00007B11* pci:v00008086d000010FBsv00001170sd0000004C* ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (82599 DP 10G Mezzanine Adapter) +pci:v00008086d000010FBsv000015D9sd00000611* + ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (AOC-STGN-I2S [REV 1.01]) + pci:v00008086d000010FBsv00001734sd000011A9* ID_MODEL_FROM_DATABASE=82599ES 10-Gigabit SFI/SFP+ Network Connection (10 Gigabit Dual Port Network Connection) @@ -73355,6 +73580,9 @@ pci:v00008086d0000156F* pci:v00008086d0000156Fsv00001028sd000006DC* ID_MODEL_FROM_DATABASE=Ethernet Connection I219-LM (Latitude E7470) +pci:v00008086d0000156Fsv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Ethernet Connection I219-LM (EliteBook 840 G3) + pci:v00008086d00001570* ID_MODEL_FROM_DATABASE=Ethernet Connection I219-V @@ -73694,6 +73922,9 @@ pci:v00008086d0000158Bsv00008086sd00000008* pci:v00008086d0000158Bsv00008086sd00000009* ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet 25G 2P XXV710 Adapter) +pci:v00008086d0000158Bsv00008086sd0000000A* + ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet 25G 2P XXV710 OCP) + pci:v00008086d0000158Bsv00008086sd00004001* ID_MODEL_FROM_DATABASE=Ethernet Controller XXV710 for 25GbE SFP28 (Ethernet Network Adapter XXV710-2) @@ -73937,6 +74168,21 @@ pci:v00008086d000015F6* pci:v00008086d000015FF* ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T +pci:v00008086d000015FFsv00008086sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T (Ethernet Network Adapter X710-TL) + +pci:v00008086d000015FFsv00008086sd00000001* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T (Ethernet Network Adapter X710-T4L) + +pci:v00008086d000015FFsv00008086sd00000002* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T (Ethernet Network Adapter X710-T4L) + +pci:v00008086d000015FFsv00008086sd00000003* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T (Ethernet Network Adapter X710-T2L) + +pci:v00008086d000015FFsv00008086sd00000004* + ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T (Ethernet Network Adapter X710-T2L) + pci:v00008086d000015FFsv00008086sd00000005* ID_MODEL_FROM_DATABASE=Ethernet Controller X710 for 10GBASE-T (Ethernet 10G 2P X710-T2L-t Adapter) @@ -74096,6 +74342,9 @@ pci:v00008086d00001904sv00001028sd000006DC* pci:v00008086d00001904sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (Latitude 3570) +pci:v00008086d00001904sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (EliteBook 840 G3) + pci:v00008086d00001904sv000017AAsd0000382A* ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers (B51-80 Laptop) @@ -74150,6 +74399,9 @@ pci:v00008086d00001916sv00001028sd000006DC* pci:v00008086d00001916sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Skylake GT2 [HD Graphics 520] (Latitude 3570) +pci:v00008086d00001916sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Skylake GT2 [HD Graphics 520] (EliteBook 840 G3) + pci:v00008086d00001918* ID_MODEL_FROM_DATABASE=Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers @@ -75827,6 +76079,9 @@ pci:v00008086d0000208D* pci:v00008086d0000208E* ID_MODEL_FROM_DATABASE=Sky Lake-E CHA Registers +pci:v00008086d00002241* + ID_MODEL_FROM_DATABASE=Larrabee + pci:v00008086d00002250* ID_MODEL_FROM_DATABASE=Xeon Phi coprocessor 5100 series @@ -79550,6 +79805,9 @@ pci:v00008086d00002701sv00001028sd00002001* pci:v00008086d00002701sv00001028sd00002002* ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (Express Flash NVMe [Optane] 750GB AIC (P4800X)) +pci:v00008086d00002701sv00001028sd0000200A* + ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (Express Flash NVMe [Optane] 375GB AIC (P4800X)) + pci:v00008086d00002701sv00008086sd00003904* ID_MODEL_FROM_DATABASE=NVMe Datacenter SSD [Optane] (x4 AIC (P4800X)) @@ -84755,6 +85013,9 @@ pci:v00008086d0000373F* pci:v00008086d000037C8* ID_MODEL_FROM_DATABASE=C62x Chipset QuickAssist Technology +pci:v00008086d000037CC* + ID_MODEL_FROM_DATABASE=Ethernet Connection X722 + pci:v00008086d000037CD* ID_MODEL_FROM_DATABASE=Ethernet Virtual Function 700 Series @@ -85982,12 +86243,18 @@ pci:v00008086d00003E92* pci:v00008086d00003E93* ID_MODEL_FROM_DATABASE=UHD Graphics 610 +pci:v00008086d00003E98* + ID_MODEL_FROM_DATABASE=UHD Graphics 630 (Desktop 9 Series) + pci:v00008086d00003E9B* ID_MODEL_FROM_DATABASE=UHD Graphics 630 (Mobile) pci:v00008086d00003EA0* ID_MODEL_FROM_DATABASE=UHD Graphics 620 (Whiskey Lake) +pci:v00008086d00003EA0sv00001028sd0000089E* + ID_MODEL_FROM_DATABASE=UHD Graphics 620 (Whiskey Lake) (Inspiron 5482) + pci:v00008086d00003EA5* ID_MODEL_FROM_DATABASE=Iris Plus Graphics 655 @@ -87542,6 +87809,9 @@ pci:v00008086d000071A2* pci:v00008086d000071A2sv00004C53sd00001000* ID_MODEL_FROM_DATABASE=440GX - 82443GX Host bridge (AGP disabled) (CC7/CR7/CP7/VC7/VP7/VR7 mainboard) +pci:v00008086d00007360* + ID_MODEL_FROM_DATABASE=XMM7360 LTE Advanced Modem + pci:v00008086d00007600* ID_MODEL_FROM_DATABASE=82372FB PIIX5 ISA @@ -88757,6 +89027,9 @@ pci:v00008086d00009D03sv00001028sd000006DC* pci:v00008086d00009D03sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (Latitude 3570) +pci:v00008086d00009D03sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (EliteBook 840 G3) + pci:v00008086d00009D03sv000017AAsd0000225D* ID_MODEL_FROM_DATABASE=Sunrise Point-LP SATA Controller [AHCI mode] (ThinkPad T480) @@ -88766,9 +89039,15 @@ pci:v00008086d00009D03sv000017AAsd0000382A* pci:v00008086d00009D10* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #1 +pci:v00008086d00009D11* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #2 + pci:v00008086d00009D12* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #3 +pci:v00008086d00009D13* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #4 + pci:v00008086d00009D14* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #5 @@ -88796,6 +89075,9 @@ pci:v00008086d00009D18sv000017AAsd0000382A* pci:v00008086d00009D19* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #10 +pci:v00008086d00009D1A* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PCI Express Root Port #11 + pci:v00008086d00009D21* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC @@ -88805,6 +89087,9 @@ pci:v00008086d00009D21sv00001028sd000006DC* pci:v00008086d00009D21sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (Latitude 3570) +pci:v00008086d00009D21sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (EliteBook 840 G3) + pci:v00008086d00009D21sv000017AAsd0000224F* ID_MODEL_FROM_DATABASE=Sunrise Point-LP PMC (ThinkPad X1 Carbon 5th Gen) @@ -88823,6 +89108,9 @@ pci:v00008086d00009D23sv00001028sd000006DC* pci:v00008086d00009D23sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (Latitude 3570) +pci:v00008086d00009D23sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (EliteBook 840 G3) + pci:v00008086d00009D23sv000017AAsd00002247* ID_MODEL_FROM_DATABASE=Sunrise Point-LP SMBus (ThinkPad T570) @@ -88859,6 +89147,9 @@ pci:v00008086d00009D2Fsv00001028sd000006DC* pci:v00008086d00009D2Fsv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (Latitude 3570) +pci:v00008086d00009D2Fsv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (EliteBook 840 G3) + pci:v00008086d00009D2Fsv000017AAsd00002247* ID_MODEL_FROM_DATABASE=Sunrise Point-LP USB 3.0 xHCI Controller (ThinkPad T570) @@ -88877,6 +89168,9 @@ pci:v00008086d00009D31sv00001028sd000006DC* pci:v00008086d00009D31sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (Latitude 3570) +pci:v00008086d00009D31sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (EliteBook 840 G3) + pci:v00008086d00009D31sv000017AAsd00002247* ID_MODEL_FROM_DATABASE=Sunrise Point-LP Thermal subsystem (ThinkPad T570) @@ -88901,6 +89195,9 @@ pci:v00008086d00009D3Asv00001028sd000006DC* pci:v00008086d00009D3Asv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (Latitude 3570) +pci:v00008086d00009D3Asv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (EliteBook 840 G3) + pci:v00008086d00009D3Asv000017AAsd00002247* ID_MODEL_FROM_DATABASE=Sunrise Point-LP CSME HECI #1 (ThinkPad T570) @@ -88916,6 +89213,9 @@ pci:v00008086d00009D3Asv000017AAsd0000382A* pci:v00008086d00009D3D* ID_MODEL_FROM_DATABASE=Sunrise Point-LP Active Management Technology - SOL +pci:v00008086d00009D3Dsv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Active Management Technology - SOL (EliteBook 840 G3) + pci:v00008086d00009D43* ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller @@ -88931,6 +89231,9 @@ pci:v00008086d00009D48sv00001028sd000006DC* pci:v00008086d00009D48sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (Latitude 3570) +pci:v00008086d00009D48sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP LPC Controller (EliteBook 840 G3) + pci:v00008086d00009D4E* ID_MODEL_FROM_DATABASE=Sunrise Point LPC Controller/eSPI Controller @@ -88958,6 +89261,9 @@ pci:v00008086d00009D60* pci:v00008086d00009D60sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (Latitude 3570) +pci:v00008086d00009D60sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (EliteBook 840 G3) + pci:v00008086d00009D60sv000017AAsd0000225D* ID_MODEL_FROM_DATABASE=Sunrise Point-LP Serial IO I2C Controller #0 (ThinkPad T480) @@ -88991,6 +89297,9 @@ pci:v00008086d00009D70sv00001028sd000006DC* pci:v00008086d00009D70sv00001028sd000006F3* ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (Latitude 3570) +pci:v00008086d00009D70sv0000103Csd00008079* + ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (EliteBook 840 G3) + pci:v00008086d00009D70sv000017AAsd0000382A* ID_MODEL_FROM_DATABASE=Sunrise Point-LP HD Audio (B51-80 Laptop) @@ -89003,6 +89312,9 @@ pci:v00008086d00009D71sv000017AAsd0000225D* pci:v00008086d00009D84* ID_MODEL_FROM_DATABASE=Cannon Point-LP LPC Controller +pci:v00008086d00009D84sv00001028sd0000089E* + ID_MODEL_FROM_DATABASE=Cannon Point-LP LPC Controller (Inspiron 5482) + pci:v00008086d00009DA3* ID_MODEL_FROM_DATABASE=Cannon Point-LP SMBus Controller @@ -89012,6 +89324,12 @@ pci:v00008086d00009DA4* pci:v00008086d00009DB0* ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #9 +pci:v00008086d00009DB4* + ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #13 + +pci:v00008086d00009DB4sv00001028sd0000089E* + ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #13 (Inspiron 5482) + pci:v00008086d00009DB6* ID_MODEL_FROM_DATABASE=Cannon Point-LP PCI Express Root Port #15 @@ -89024,12 +89342,27 @@ pci:v00008086d00009DBC* pci:v00008086d00009DC8* ID_MODEL_FROM_DATABASE=Cannon Point-LP High Definition Audio Controller +pci:v00008086d00009DC8sv00001028sd0000089E* + ID_MODEL_FROM_DATABASE=Cannon Point-LP High Definition Audio Controller (Inspiron 5482) + pci:v00008086d00009DD3* ID_MODEL_FROM_DATABASE=Cannon Point-LP SATA Controller [AHCI Mode] pci:v00008086d00009DE0* ID_MODEL_FROM_DATABASE=Cannon Point-LP MEI Controller #1 +pci:v00008086d00009DE8* + ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Controller #0 + +pci:v00008086d00009DE8sv00001028sd0000089E* + ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Controller #0 (Inspiron 5482) + +pci:v00008086d00009DE9* + ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Controller #1 + +pci:v00008086d00009DE9sv00001028sd0000089E* + ID_MODEL_FROM_DATABASE=Cannon Point-LP Serial IO I2C Controller #1 (Inspiron 5482) + pci:v00008086d00009DED* ID_MODEL_FROM_DATABASE=Cannon Point-LP USB 3.1 xHCI Controller @@ -89042,6 +89375,9 @@ pci:v00008086d00009DF0* pci:v00008086d00009DF9* ID_MODEL_FROM_DATABASE=Cannon Point-LP Thermal Controller +pci:v00008086d00009DFC* + ID_MODEL_FROM_DATABASE=Cannon Point-LP Integrated Sensor Hub + pci:v00008086d0000A000* ID_MODEL_FROM_DATABASE=Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge @@ -89999,6 +90335,27 @@ pci:v00008086d0000F1A5* pci:v00008086d0000F1A6* ID_MODEL_FROM_DATABASE=SSD Pro 7600p/760p/E 6100p Series +pci:v00008086d0000F1A8* + ID_MODEL_FROM_DATABASE=SSDPEKNW020T8 [660p, 2TB] + +pci:v00008086d0000F1A8sv00008086sd0000390D* + ID_MODEL_FROM_DATABASE=SSDPEKNW020T8 [660p, 2TB] + +pci:v00008088* + ID_VENDOR_FROM_DATABASE=Beijing Wangxun Technology Co., Ltd. + +pci:v00008088d00001001* + ID_MODEL_FROM_DATABASE=Ethernet Controller RP1000 for 10GbE SFP+ + +pci:v00008088d00001001sv00008088sd00000000* + ID_MODEL_FROM_DATABASE=Ethernet Controller RP1000 for 10GbE SFP+ (Ethernet Network Adaptor RP1000 for 10GbE SFP+) + +pci:v00008088d00002001* + ID_MODEL_FROM_DATABASE=Ethernet Controller RP2000 for 10GbE SFP+ + +pci:v00008088d00002001sv00008088sd00002000* + ID_MODEL_FROM_DATABASE=Ethernet Controller RP2000 for 10GbE SFP+ (Ethernet Network Adaptor RP2000 for 10GbE SFP+) + pci:v000080EE* ID_VENDOR_FROM_DATABASE=InnoTek Systemberatung GmbH @@ -91013,6 +91370,15 @@ pci:v00009005d0000028Fsv0000103Csd00001100* pci:v00009005d0000028Fsv0000103Csd00001101* ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (Smart Array P416ie-m SR G10) +pci:v00009005d0000028Fsv0000105Bsd00001211* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (HBA 8238-16i) + +pci:v00009005d0000028Fsv0000105Bsd00001321* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (HBA 8242-24i) + +pci:v00009005d0000028Fsv000013FEsd00008312* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SKY-9200 MIC-8312BridgeB) + pci:v00009005d0000028Fsv0000152Dsd00008A22* ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (QS-8204-8i) @@ -91028,6 +91394,66 @@ pci:v00009005d0000028Fsv0000152Dsd00008A36* pci:v00009005d0000028Fsv0000152Dsd00008A37* ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (QS-8242-24i) +pci:v00009005d0000028Fsv0000193Dsd00008460* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (HBA H460-M1) + +pci:v00009005d0000028Fsv0000193Dsd00008461* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (HBA H460-B1) + +pci:v00009005d0000028Fsv0000193Dsd0000C460* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID P460-M2) + +pci:v00009005d0000028Fsv0000193Dsd0000C461* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID P460-B2) + +pci:v00009005d0000028Fsv0000193Dsd0000F460* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID P460-M4) + +pci:v00009005d0000028Fsv0000193Dsd0000F461* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID P460-B4) + +pci:v00009005d0000028Fsv000019E5sd0000D227* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartROC-HD SR465C-M 4G) + +pci:v00009005d0000028Fsv000019E5sd0000D228* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartROC SR455C-M 2G) + +pci:v00009005d0000028Fsv000019E5sd0000D229* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartIOC SR155-M) + +pci:v00009005d0000028Fsv000019E5sd0000D22A* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartIOC-HD SR765-M) + +pci:v00009005d0000028Fsv000019E5sd0000D22B* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartROC-e SR455C-ME 4G) + +pci:v00009005d0000028Fsv000019E5sd0000D22C* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartROC SR455C-M 4G) + +pci:v00009005d0000028Fsv00001BD4sd00000045* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SMART-HBA 8242-24i) + +pci:v00009005d0000028Fsv00001BD4sd00000046* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID 8236-16i) + +pci:v00009005d0000028Fsv00001BD4sd00000047* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID 8240-24i) + +pci:v00009005d0000028Fsv00001BD4sd00000048* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SMART-HBA 8238-16i) + +pci:v00009005d0000028Fsv00001BD4sd0000004A* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (PM8222-SHBA) + +pci:v00009005d0000028Fsv00001BD4sd0000004B* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID PM8204-2GB) + +pci:v00009005d0000028Fsv00001BD4sd0000004C* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (RAID PM8204-4GB) + +pci:v00009005d0000028Fsv00001BD4sd0000004F* + ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (PM8222-HBA) + pci:v00009005d0000028Fsv00009005sd00000608* ID_MODEL_FROM_DATABASE=Smart Storage PQI 12G SAS/PCIe 3 (SmartRAID 3162-8i /e) @@ -91679,6 +92105,9 @@ pci:v0000BDBDd0000A148* pci:v0000BDBDd0000A14B* ID_MODEL_FROM_DATABASE=DeckLink 8K Pro +pci:v0000BDBDd0000A14E* + ID_MODEL_FROM_DATABASE=DeckLink Quad HDMI Recorder + pci:v0000BDBDd0000A1FF* ID_MODEL_FROM_DATABASE=eGPU RX580 @@ -92375,6 +92804,9 @@ pci:v0000F1D0d0000DCAF* pci:v0000F1D0d0000DFEE* ID_MODEL_FROM_DATABASE=Xena HD-DA +pci:v0000F1D0d0000EB0D* + ID_MODEL_FROM_DATABASE=Corvid 88 + pci:v0000F1D0d0000EB0E* ID_MODEL_FROM_DATABASE=Corvid 44 diff --git a/hwdb/20-usb-vendor-model.hwdb b/hwdb/20-usb-vendor-model.hwdb index f14aa703d..3bb26adaa 100644 --- a/hwdb/20-usb-vendor-model.hwdb +++ b/hwdb/20-usb-vendor-model.hwdb @@ -449,6 +449,9 @@ usb:v03EBp7800* usb:v03EBp800C* ID_MODEL_FROM_DATABASE=Airspy HF+ +usb:v03EBpFF02* + ID_MODEL_FROM_DATABASE=WootingTwo + usb:v03EBpFF07* ID_MODEL_FROM_DATABASE=Tux Droid fish dongle @@ -512,6 +515,9 @@ usb:v03F0p0024* usb:v03F0p002A* ID_MODEL_FROM_DATABASE=LaserJet P1102 +usb:v03F0p0053* + ID_MODEL_FROM_DATABASE=DeskJet 2620 All-in-One Printer + usb:v03F0p0101* ID_MODEL_FROM_DATABASE=ScanJet 4100c @@ -674,6 +680,9 @@ usb:v03F0p0517* usb:v03F0p051D* ID_MODEL_FROM_DATABASE=Bluetooth Interface +usb:v03F0p052A* + ID_MODEL_FROM_DATABASE=LaserJet M1212nf MFP + usb:v03F0p0601* ID_MODEL_FROM_DATABASE=ScanJet 6300c @@ -3809,6 +3818,9 @@ usb:v0419p0001* usb:v0419p0600* ID_MODEL_FROM_DATABASE=Desktop Wireless 6000 +usb:v0419p2694* + ID_MODEL_FROM_DATABASE=Laila + usb:v0419p3001* ID_MODEL_FROM_DATABASE=Xerox P1202 Laser Printer @@ -4772,9 +4784,6 @@ usb:v0424p223A* usb:v0424p2503* ID_MODEL_FROM_DATABASE=USB 2.0 Hub -usb:v0424p2504* - ID_MODEL_FROM_DATABASE=USB 2.0 Hub - usb:v0424p2507* ID_MODEL_FROM_DATABASE=hub @@ -4808,6 +4817,9 @@ usb:v0424p2744* usb:v0424p274D* ID_MODEL_FROM_DATABASE=HTC Hub Controller +usb:v0424p2807* + ID_MODEL_FROM_DATABASE=Hub + usb:v0424p3FCC* ID_MODEL_FROM_DATABASE=RME MADIface @@ -4829,6 +4841,9 @@ usb:v0424p5534* usb:v0424p5744* ID_MODEL_FROM_DATABASE=Hub +usb:v0424p5807* + ID_MODEL_FROM_DATABASE=Hub + usb:v0424p7500* ID_MODEL_FROM_DATABASE=LAN7500 Ethernet 10/100/1000 Adapter @@ -7154,6 +7169,9 @@ usb:v045Ep07A5* usb:v045Ep07B2* ID_MODEL_FROM_DATABASE=2.4GHz Transceiver v8.0 used by mouse Wireless Desktop 900 +usb:v045Ep07B6* + ID_MODEL_FROM_DATABASE=Comfort Curve Keyboard 3000 + usb:v045Ep07B9* ID_MODEL_FROM_DATABASE=Wired Keyboard 200 @@ -9542,6 +9560,9 @@ usb:v0480p0200* usb:v0480p0820* ID_MODEL_FROM_DATABASE=Canvio Advance Disk +usb:v0480p0821* + ID_MODEL_FROM_DATABASE=Canvio Advance 2TB model DTC920 + usb:v0480pA006* ID_MODEL_FROM_DATABASE=External Disk 1.5TB @@ -9728,6 +9749,9 @@ usb:v0483p5731* usb:v0483p5740* ID_MODEL_FROM_DATABASE=Virtual COM Port +usb:v0483p5750* + ID_MODEL_FROM_DATABASE=LED badge -- mini LED display -- 11x44 + usb:v0483p7270* ID_MODEL_FROM_DATABASE=ST Micro Serial Bridge @@ -9860,6 +9884,9 @@ usb:v048Dp9507* usb:v048Dp9910* ID_MODEL_FROM_DATABASE=IT9910 chipset based grabber +usb:v048DpFF59* + ID_MODEL_FROM_DATABASE=Hdmi-CEC Bridge + usb:v048F* ID_VENDOR_FROM_DATABASE=Eicon Tech. @@ -10061,6 +10088,9 @@ usb:v0499p160F* usb:v0499p1613* ID_MODEL_FROM_DATABASE=Clavinova CLP535 +usb:v0499p1617* + ID_MODEL_FROM_DATABASE=PSR-E353 digital keyboard + usb:v0499p1704* ID_MODEL_FROM_DATABASE=Steinberg UR44 @@ -12821,6 +12851,9 @@ usb:v04B0p0429* usb:v04B0p042A* ID_MODEL_FROM_DATABASE=D800 (ptp) +usb:v04B0p0430* + ID_MODEL_FROM_DATABASE=D7100 + usb:v04B0p043F* ID_MODEL_FROM_DATABASE=D5600 @@ -14549,6 +14582,12 @@ usb:v04D8pC001* usb:v04D8pE11C* ID_MODEL_FROM_DATABASE=TL866CS EEPROM Programmer [MiniPRO] +usb:v04D8pEDB4* + ID_MODEL_FROM_DATABASE=micro PLC (ATSAMD51G19A) [Black Brix ECU II] + +usb:v04D8pEDB5* + ID_MODEL_FROM_DATABASE=ATMEGA32U4 [Black Brix ECU] + usb:v04D8pF2C4* ID_MODEL_FROM_DATABASE=Macareux-labs Hygrometry Temperature Sensor @@ -15470,6 +15509,9 @@ usb:v04E8p342D* usb:v04E8p344F* ID_MODEL_FROM_DATABASE=SCX-3400 Series +usb:v04E8p347E* + ID_MODEL_FROM_DATABASE=C48x Series Color Laser Multifunction Printer + usb:v04E8p3605* ID_MODEL_FROM_DATABASE=InkJet Color Printer @@ -15707,6 +15749,9 @@ usb:v04E8p61B5* usb:v04E8p61B6* ID_MODEL_FROM_DATABASE=M3 Portable Hard Drive 1TB +usb:v04E8p61B7* + ID_MODEL_FROM_DATABASE=M3 Portable Hard Drive 4TB + usb:v04E8p61F3* ID_MODEL_FROM_DATABASE=Portable SSD T3 (MU-PT250B, MU-PT500B) @@ -15893,6 +15938,9 @@ usb:v04E8p7080* usb:v04E8p7081* ID_MODEL_FROM_DATABASE=Human Interface Device +usb:v04E8p7301* + ID_MODEL_FROM_DATABASE=Fingerprint Device + usb:v04E8p8001* ID_MODEL_FROM_DATABASE=Handheld @@ -16319,6 +16367,9 @@ usb:v04F3p0381* usb:v04F3p04A0* ID_MODEL_FROM_DATABASE=Dream Cheeky Stress/Panic Button +usb:v04F3p2234* + ID_MODEL_FROM_DATABASE=Touchscreen + usb:v04F4* ID_VENDOR_FROM_DATABASE=Harting Elektronik, Inc. @@ -17855,6 +17906,15 @@ usb:v04F9p2061* usb:v04F9p2064* ID_MODEL_FROM_DATABASE=PT-P700 P-touch Label Printer RemovableDisk +usb:v04F9p209B* + ID_MODEL_FROM_DATABASE=QL-800 P-touch Label Printer + +usb:v04F9p209C* + ID_MODEL_FROM_DATABASE=QL-810W P-touch Label Printer + +usb:v04F9p209D* + ID_MODEL_FROM_DATABASE=QL-820NWB P-touch Label Printer + usb:v04F9p2100* ID_MODEL_FROM_DATABASE=Card Reader Writer @@ -19103,6 +19163,9 @@ usb:v054Cp0045* usb:v054Cp0046* ID_MODEL_FROM_DATABASE=Network Walkman +usb:v054Cp0049* + ID_MODEL_FROM_DATABASE=UP-D895 + usb:v054Cp004A* ID_MODEL_FROM_DATABASE=Memory Stick Hi-Fi System @@ -19355,6 +19418,9 @@ usb:v054Cp01D5* usb:v054Cp01DE* ID_MODEL_FROM_DATABASE=VRD-VC10 [Video Capture] +usb:v054Cp01E7* + ID_MODEL_FROM_DATABASE=UP-D897 + usb:v054Cp01E8* ID_MODEL_FROM_DATABASE=UP-DR150 Photo Printer @@ -20222,6 +20288,12 @@ usb:v056Ap0038* usb:v056Ap0039* ID_MODEL_FROM_DATABASE=DTU-710 +usb:v056Ap003A* + ID_MODEL_FROM_DATABASE=DTI-520 + +usb:v056Ap003B* + ID_MODEL_FROM_DATABASE=Integrated Hub + usb:v056Ap003F* ID_MODEL_FROM_DATABASE=DTZ-2100 [Cintiq 21UX] @@ -20289,7 +20361,7 @@ usb:v056Ap0081* ID_MODEL_FROM_DATABASE=CTE-630BT [Graphire Wireless (6x8)] usb:v056Ap0084* - ID_MODEL_FROM_DATABASE=Wireless adapter for Bamboo tablets + ID_MODEL_FROM_DATABASE=ACK-40401 [Wireless Accessory Kit] usb:v056Ap0090* ID_MODEL_FROM_DATABASE=TPC90 @@ -20426,6 +20498,9 @@ usb:v056Ap00ED* usb:v056Ap00EF* ID_MODEL_FROM_DATABASE=TPCEF +usb:v056Ap00F0* + ID_MODEL_FROM_DATABASE=DTU-1631 + usb:v056Ap00F4* ID_MODEL_FROM_DATABASE=DTK-2400 [Cintiq 24HD] tablet @@ -20501,9 +20576,51 @@ usb:v056Ap0317* usb:v056Ap0318* ID_MODEL_FROM_DATABASE=CTH-301 [Bamboo] +usb:v056Ap0319* + ID_MODEL_FROM_DATABASE=CTH-300 [Bamboo Pad wireless] + +usb:v056Ap0323* + ID_MODEL_FROM_DATABASE=CTL-680 [Intuos Pen (M)] + +usb:v056Ap032A* + ID_MODEL_FROM_DATABASE=DTK-2700 [Cintiq 27QHD] + +usb:v056Ap032B* + ID_MODEL_FROM_DATABASE=DTH-2700 [Cintiq 27QHD touch] tablet + +usb:v056Ap032C* + ID_MODEL_FROM_DATABASE=DTH-2700 [Cintiq 27QHD touch] touchscreen + usb:v056Ap032F* ID_MODEL_FROM_DATABASE=DTU-1031X +usb:v056Ap0331* + ID_MODEL_FROM_DATABASE=ACK-411050 [ExpressKey Remote] + +usb:v056Ap0333* + ID_MODEL_FROM_DATABASE=DTH-1300 [Cintiq 13HD Touch] tablet + +usb:v056Ap0335* + ID_MODEL_FROM_DATABASE=DTH-1300 [Cintiq 13HD Touch] touchscreen + +usb:v056Ap0336* + ID_MODEL_FROM_DATABASE=DTU-1141 + +usb:v056Ap033B* + ID_MODEL_FROM_DATABASE=CTL-490 [Intuos Draw (S)] + +usb:v056Ap033C* + ID_MODEL_FROM_DATABASE=CTH-490 [Intuos Art/Photo/Comic (S)] + +usb:v056Ap033D* + ID_MODEL_FROM_DATABASE=CTL-690 [Intuos Draw (M)] + +usb:v056Ap033E* + ID_MODEL_FROM_DATABASE=CTH-690 [Intuos Art (M)] + +usb:v056Ap0343* + ID_MODEL_FROM_DATABASE=DTK-1651 + usb:v056Ap0347* ID_MODEL_FROM_DATABASE=Integrated Hub @@ -20552,6 +20669,9 @@ usb:v056Ap0357* usb:v056Ap0358* ID_MODEL_FROM_DATABASE=PTH-860 [Intuos Pro (L)] +usb:v056Ap0359* + ID_MODEL_FROM_DATABASE=DTU-1141B + usb:v056Ap035A* ID_MODEL_FROM_DATABASE=DTH-1152 tablet @@ -20702,6 +20822,15 @@ usb:v056Ep0075* usb:v056Ep0077* ID_MODEL_FROM_DATABASE=Laser mouse M-LY2UL +usb:v056Ep0079* + ID_MODEL_FROM_DATABASE=Laser mouse M-D21DL + +usb:v056Ep007B* + ID_MODEL_FROM_DATABASE=Laser mouse M-D20DR + +usb:v056Ep007C* + ID_MODEL_FROM_DATABASE=Laser Bluetooth mouse M-BT5BL + usb:v056Ep2003* ID_MODEL_FROM_DATABASE=JC-U3613M @@ -27992,6 +28121,9 @@ usb:v06F8pA300* usb:v06F8pB000* ID_MODEL_FROM_DATABASE=Hercules DJ Console +usb:v06F8pB121* + ID_MODEL_FROM_DATABASE=Hercules P32 DJ + usb:v06F8pC000* ID_MODEL_FROM_DATABASE=Hercules Muse Pocket @@ -36131,6 +36263,15 @@ usb:v0AADp0083* usb:v0AADp0095* ID_MODEL_FROM_DATABASE=NRP-Z86 +usb:v0AADp0117* + ID_MODEL_FROM_DATABASE=HMF / HMP / HMS-X / HMO series Oscilloscopes + +usb:v0AADp0118* + ID_MODEL_FROM_DATABASE=HMF / HMP / HMS-X / HMO series Oscilloscopes + +usb:v0AADp0119* + ID_MODEL_FROM_DATABASE=HMF / HMP / HMS-X / HMO series Oscilloscopes + usb:v0AAE* ID_VENDOR_FROM_DATABASE=NEC infrontia Corp. (Nitsuko) @@ -57092,6 +57233,15 @@ usb:v22B9p0006* usb:v22BA* ID_VENDOR_FROM_DATABASE=Technology Innovation Holdings, Ltd +usb:v22E0* + ID_VENDOR_FROM_DATABASE=secunet Security Networks AG + +usb:v22E0p0002* + ID_MODEL_FROM_DATABASE=SINA Flash Drive + +usb:v22E0p0003* + ID_MODEL_FROM_DATABASE=SINA ID Token A + usb:v2304* ID_VENDOR_FROM_DATABASE=Pinnacle Systems, Inc. @@ -57500,6 +57650,51 @@ usb:v2676* usb:v2676pBA02* ID_MODEL_FROM_DATABASE=ace +usb:v2717* + ID_VENDOR_FROM_DATABASE=Xiaomi Inc. + +usb:v2717p0011* + ID_MODEL_FROM_DATABASE=100Mbps Network Card Adapter + +usb:v2717p0360* + ID_MODEL_FROM_DATABASE=Mi3W + +usb:v2717p0368* + ID_MODEL_FROM_DATABASE=Mi4 LTE + +usb:v2717p3801* + ID_MODEL_FROM_DATABASE=Mi ANC & Type-C In-Ear Earphones + +usb:v2717p4106* + ID_MODEL_FROM_DATABASE=MediaTek MT7601U [MI WiFi] + +usb:v2717pFF08* + ID_MODEL_FROM_DATABASE=Redmi Note 3 (ADB Interface) + +usb:v2717pFF10* + ID_MODEL_FROM_DATABASE=Mi/Redmi series (PTP) + +usb:v2717pFF18* + ID_MODEL_FROM_DATABASE=Mi/Redmi series (PTP + ADB) + +usb:v2717pFF40* + ID_MODEL_FROM_DATABASE=Mi/Redmi series (MTP) + +usb:v2717pFF48* + ID_MODEL_FROM_DATABASE=Mi/Redmi series (MTP + ADB) + +usb:v2717pFF60* + ID_MODEL_FROM_DATABASE=redmi prime 2 + +usb:v2717pFF68* + ID_MODEL_FROM_DATABASE=Mi-4c + +usb:v2717pFF80* + ID_MODEL_FROM_DATABASE=Mi/Redmi series (RNDIS) + +usb:v2717pFF88* + ID_MODEL_FROM_DATABASE=Mi/Redmi series (RNDIS + ADB) + usb:v2730* ID_VENDOR_FROM_DATABASE=Citizen diff --git a/hwdb/60-evdev.hwdb b/hwdb/60-evdev.hwdb index 3ebdeb656..4f94342a0 100644 --- a/hwdb/60-evdev.hwdb +++ b/hwdb/60-evdev.hwdb @@ -100,6 +100,32 @@ evdev:input:b0003v05ACp025B* EVDEV_ABS_35=::94 EVDEV_ABS_36=::92 +# MacBook8,1 (2015), MacBook9,1 (2016), MacBook10,1 (2017) +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBook8,1:* +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBook9,1:* +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBook10,1:* + EVDEV_ABS_00=::95 + EVDEV_ABS_01=::90 + EVDEV_ABS_35=::95 + EVDEV_ABS_36=::90 + +# MacBookPro13,* (Late 2016), MacBookPro14,* (Mid 2017) +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBookPro13,1:* +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBookPro13,2:* +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBookPro14,1:* +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBookPro14,2:* + EVDEV_ABS_00=::96 + EVDEV_ABS_01=::94 + EVDEV_ABS_35=::96 + EVDEV_ABS_36=::94 + +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBookPro13,3:* +evdev:name:Apple SPI Touchpad:dmi:*:svnAppleInc.:pnMacBookPro14,3:* + EVDEV_ABS_00=::96 + EVDEV_ABS_01=::95 + EVDEV_ABS_35=::96 + EVDEV_ABS_36=::95 + ######################################### # ASUS ######################################### diff --git a/hwdb/60-input-id.hwdb b/hwdb/60-input-id.hwdb index 7e9dc9922..20c1e7ea0 100644 --- a/hwdb/60-input-id.hwdb +++ b/hwdb/60-input-id.hwdb @@ -66,3 +66,7 @@ id-input:modalias:input:b0003v5543p0081* # XP-PEN STAR 06 id-input:modalias:input:b0003v28bdp0078* ID_INPUT_TABLET=1 + +# Lite-On Tech IBM USB Travel Keyboard with Ultra Nav Mouse +id-input:modalias:input:b0003v04B3p301Ee0100-e0,1,2,4* + ID_INPUT_POINTINGSTICK=1 diff --git a/hwdb/60-keyboard.hwdb b/hwdb/60-keyboard.hwdb index 355544030..71aecd8a5 100644 --- a/hwdb/60-keyboard.hwdb +++ b/hwdb/60-keyboard.hwdb @@ -188,6 +188,7 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAcer*:pnAOA*:pvr* ########################################################### evdev:atkbd:dmi:bvn*:bvr*:bd*:svnAlienware*:pn* + KEYBOARD_KEY_81=f21 # Touchpad toggle KEYBOARD_KEY_8a=ejectcd # Alienware/Dell reserves these keys; safe to apply on all their devices @@ -210,6 +211,10 @@ evdev:name:Eee PC WMI hotkeys:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:pvr* evdev:name:Asus Laptop extra buttons:dmi:bvn*:bvr*:bd*:svnASUS*:pn*:pvr* KEYBOARD_KEY_6b=f21 # Touchpad Toggle +# USB keyboard in Asus FX503VD +evdev:input:b0003v0B05p1869* + KEYBOARD_KEY_ff31007c=f20 # Remap micmute to f20 + ########################################################### # BenQ ########################################################### @@ -499,7 +504,8 @@ evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pnHP*Pavilion*dv7*Notebook*PC: KEYBOARD_KEY_c6=break KEYBOARD_KEY_94=reserved -# Pavilion x360 13 (Prevents random airplane mode activation) +# Pavilion and Spectre x360 13 (Prevents random airplane mode activation) +evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[sS][pP][eE][cC][tT][rR][eE]*x360*13*:pvr* evdev:atkbd:dmi:bvn*:bvr*:bd*:svnHewlett-Packard*:pn*[pP][aA][vV][iI][lL][iI][oO][nN]*13*x360*:pvr* KEYBOARD_KEY_d7=unknown @@ -802,6 +808,10 @@ evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*IdeaPad*Z370*:pvr* KEYBOARD_KEY_ae=!volumedown KEYBOARD_KEY_b0=!volumeup +# Lenovo Y50-70 +evdev:atkbd:dmi:bvn*:bvr*:svnLENOVO*:pn*20378*:pvr* + KEYBOARD_KEY_f3=f21 # Fn+F6 (toggle touchpad) + # V480 evdev:atkbd:dmi:bvn*:bvr*:bd*:svnLENOVO*:pn*Lenovo*V480*:pvr* KEYBOARD_KEY_f1=f21 diff --git a/hwdb/60-sensor.hwdb b/hwdb/60-sensor.hwdb index b916addca..1a6f2d875 100644 --- a/hwdb/60-sensor.hwdb +++ b/hwdb/60-sensor.hwdb @@ -71,6 +71,9 @@ sensor:modalias:acpi:INVN6500*:dmi:*svn*Acer*:*pn*AspireSW5-012* sensor:modalias:acpi:BMA250E*:dmi:*:svnAcer:pnIconiaW1-810:* ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 +sensor:modalias:acpi:SMO8500:*:dmi:*Acer*:pnOneS1002* + 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 @@ -358,12 +361,26 @@ sensor:modalias:acpi:BOSC0200*:dmi:*:svnLINX*:pnLINX1010B:* sensor:modalias:acpi:KIOX000A*:dmi:*:svnLINX*:pnLINX12X64:* ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 +######################################### +# Medion +######################################### +sensor:modalias:acpi:SMO8500*:dmi:*:svnMEDION:pnAkoyaE2212TMD99720:* + ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 + ######################################### # MSI ######################################### sensor:modalias:acpi:SMO8500*:dmi:*:svnMicro-StarInternationalCo.,Ltd.:pnS100:* ACCEL_MOUNT_MATRIX=0, -1, 0; 1, 0, 0; 0, 0, 1 +######################################### +# MYRIA +######################################### + +# MY8307 +sensor:modalias:acpi:BOSC0200*:dmi:*:svnCompletElectroServ:pnMY8307:* + ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, 1, 0; 0, 0, 1 + ######################################### # Nuvision (TMax) ######################################### @@ -459,6 +476,10 @@ sensor:modalias:acpi:KIOX000A*:dmi:bvnAmericanMegatrendsInc.:bvr5.011:bd11/03/20 sensor:modalias:acpi:KIOX010A*:dmi:*:svnTECLAST:pnF5:* ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 +# Teclast F6 Pro +sensor:modalias:acpi:KIOX010A*:dmi:*:svnTECLAST:pnF6Pro:* + ACCEL_MOUNT_MATRIX=-1, 0, 0; 0, -1, 0; 0, 0, -1 + ######################################### # Trekstor ######################################### @@ -467,6 +488,7 @@ sensor:modalias:acpi:BMA250*:dmi:*:bvrTREK.G.WI71C.JGBMRBA*:*:svnTrekStor:pnSurf ACCEL_MOUNT_MATRIX=0, 1, 0; 1, 0, 0; 0, 0, 1 sensor:modalias:acpi:KIOX000A*:dmi:*:svnTREKSTOR:pnPrimetabT13B:* +sensor:modalias:acpi:BOSC0200*:dmi:*:svnTrekStor*:pnSurfTabtwin11.6:* ACCEL_MOUNT_MATRIX=1, 0, 0; 0, -1, 0; 0, 0, 1 ######################################### diff --git a/hwdb/70-mouse.hwdb b/hwdb/70-mouse.hwdb index 98549039f..0eb5c9d22 100644 --- a/hwdb/70-mouse.hwdb +++ b/hwdb/70-mouse.hwdb @@ -681,7 +681,7 @@ mouse:usb:v0425p0101:name:G-Tech CHINA USB Wireless Mouse & KeyBoard V1.01 : # Razer Abyssus mouse:usb:v1532p0042:name:Razer Razer Abyssus: - MOUSE_DPI=3500@1000 + MOUSE_DPI=1600@1000 # Razer DeathAdder Black Edition mouse:usb:v1532p0029:name:Razer Razer DeathAdder: diff --git a/hwdb/70-touchpad.hwdb b/hwdb/70-touchpad.hwdb index 979635357..b16da9604 100644 --- a/hwdb/70-touchpad.hwdb +++ b/hwdb/70-touchpad.hwdb @@ -48,3 +48,11 @@ touchpad:bluetooth:* touchpad:usb:v056a* ID_INPUT_TOUCHPAD_INTEGRATION=external +########################################################### +# Apple +########################################################### +# Magic Trackpad (1 and 2) +touchpad:usb:v05acp030e:* +touchpad:usb:v05acp0265:* + ID_INPUT_TOUCHPAD_INTEGRATION=external + diff --git a/hwdb/acpi-update.py b/hwdb/acpi-update.py index 2c23b057f..7bdd3f93d 100755 --- a/hwdb/acpi-update.py +++ b/hwdb/acpi-update.py @@ -72,8 +72,8 @@ b = open("pnp_id_registry.html") print('# This file is part of systemd.\n' '#\n' '# Data imported from:\n' - '# http://www.uefi.org/uefi-pnp-export\n' - '# http://www.uefi.org/uefi-acpi-export') + '# https://uefi.org/uefi-pnp-export\n' + '# https://uefi.org/uefi-acpi-export') read_table(a) read_table(b) diff --git a/hwdb/ids_parser.py b/hwdb/ids_parser.py index 61ca0ca6e..58ecc73ae 100755 --- a/hwdb/ids_parser.py +++ b/hwdb/ids_parser.py @@ -5,6 +5,7 @@ import sys from pyparsing import (Word, White, Literal, Regex, LineEnd, SkipTo, ZeroOrMore, OneOrMore, Combine, Optional, Suppress, + Group, stringEnd, pythonStyleComment) EOL = LineEnd().suppress() @@ -23,10 +24,10 @@ def klass_grammar(): subclass_line = TAB + NUM2('subclass') + text_eol('text') protocol_line = TAB + TAB + NUM2('protocol') + text_eol('name') subclass = (subclass_line('SUBCLASS') - - ZeroOrMore(protocol_line('PROTOCOLS*') + ZeroOrMore(Group(protocol_line)('PROTOCOLS*') ^ COMMENTLINE.suppress())) klass = (klass_line('KLASS') - - ZeroOrMore(subclass('SUBCLASSES*') + ZeroOrMore(Group(subclass)('SUBCLASSES*') ^ COMMENTLINE.suppress())) return klass @@ -34,7 +35,7 @@ def usb_ids_grammar(): vendor_line = NUM4('vendor') + text_eol('text') device_line = TAB + NUM4('device') + text_eol('text') vendor = (vendor_line('VENDOR') + - ZeroOrMore(device_line('VENDOR_DEV*') ^ COMMENTLINE.suppress())) + ZeroOrMore(Group(device_line)('VENDOR_DEV*') ^ COMMENTLINE.suppress())) klass = klass_grammar() @@ -44,7 +45,8 @@ def usb_ids_grammar(): other_group = (other_line - ZeroOrMore(TAB + text_eol('text'))) commentgroup = OneOrMore(COMMENTLINE).suppress() ^ EMPTYLINE.suppress() - grammar = OneOrMore(vendor('VENDORS*') ^ klass('CLASSES*') + grammar = OneOrMore(Group(vendor)('VENDORS*') + ^ Group(klass)('CLASSES*') ^ other_group.suppress() ^ commentgroup) + stringEnd() grammar.parseWithTabs() @@ -56,14 +58,15 @@ def pci_ids_grammar(): subvendor_line = TAB + TAB + NUM4('a') + White(' ') + NUM4('b') + text_eol('name') device = (device_line('DEVICE') + - ZeroOrMore(subvendor_line('SUBVENDORS*') ^ COMMENTLINE.suppress())) + ZeroOrMore(Group(subvendor_line)('SUBVENDORS*') ^ COMMENTLINE.suppress())) vendor = (vendor_line('VENDOR') + - ZeroOrMore(device('DEVICES*') ^ COMMENTLINE.suppress())) + ZeroOrMore(Group(device)('DEVICES*') ^ COMMENTLINE.suppress())) klass = klass_grammar() commentgroup = OneOrMore(COMMENTLINE).suppress() ^ EMPTYLINE.suppress() - grammar = OneOrMore(vendor('VENDORS*') ^ klass('CLASSES*') + grammar = OneOrMore(Group(vendor)('VENDORS*') + ^ Group(klass)('CLASSES*') ^ commentgroup) + stringEnd() grammar.parseWithTabs() @@ -73,12 +76,14 @@ def sdio_ids_grammar(): vendor_line = NUM4('vendor') + text_eol('text') device_line = TAB + NUM4('device') + text_eol('text') vendor = (vendor_line('VENDOR') + - ZeroOrMore(device_line('DEVICES*') ^ COMMENTLINE.suppress())) + ZeroOrMore(Group(device_line)('DEVICES*') ^ COMMENTLINE.suppress())) klass = klass_grammar() commentgroup = OneOrMore(COMMENTLINE).suppress() ^ EMPTYLINE.suppress() - grammar = OneOrMore(vendor('VENDORS*') ^ klass('CLASSES*') ^ commentgroup) + stringEnd() + grammar = OneOrMore(Group(vendor)('VENDORS*') + ^ Group(klass)('CLASSES*') + ^ commentgroup) + stringEnd() grammar.parseWithTabs() return grammar @@ -102,7 +107,7 @@ def oui_grammar(type): grammar = (Literal('OUI') + text_eol('header') + text_eol('header') + text_eol('header') + EMPTYLINE - + OneOrMore(vendor('VENDORS*')) + stringEnd()) + + OneOrMore(Group(vendor)('VENDORS*')) + stringEnd()) grammar.parseWithTabs() return grammar @@ -126,8 +131,8 @@ def usb_vendor_model(p): items = {} for vendor_group in p.VENDORS: - vendor = vendor_group.VENDOR.vendor.upper() - text = vendor_group.VENDOR.text.strip() + vendor = vendor_group.vendor.upper() + text = vendor_group.text.strip() add_item(items, (vendor,), text) for vendor_dev in vendor_group.VENDOR_DEV: @@ -152,8 +157,8 @@ def usb_classes(p): items = {} for klass_group in p.CLASSES: - klass = klass_group.KLASS.klass.upper() - text = klass_group.KLASS.text.strip() + klass = klass_group.klass.upper() + text = klass_group.text.strip() if klass != '00' and not re.match(r'(\?|None|Unused)\s*$', text): add_item(items, (klass,), text) @@ -189,8 +194,8 @@ def pci_vendor_model(p): items = {} for vendor_group in p.VENDORS: - vendor = vendor_group.VENDOR.vendor.upper() - text = vendor_group.VENDOR.text.strip() + vendor = vendor_group.vendor.upper() + text = vendor_group.text.strip() add_item(items, (vendor,), text) for device_group in vendor_group.DEVICES: @@ -227,8 +232,8 @@ def pci_classes(p): items = {} for klass_group in p.CLASSES: - klass = klass_group.KLASS.klass.upper() - text = klass_group.KLASS.text.strip() + klass = klass_group.klass.upper() + text = klass_group.text.strip() add_item(items, (klass,), text) for subclass_group in klass_group.SUBCLASSES: @@ -260,8 +265,8 @@ def sdio_vendor_model(p): items = {} for vendor_group in p.VENDORS: - vendor = vendor_group.VENDOR.vendor.upper() - text = vendor_group.VENDOR.text.strip() + vendor = vendor_group.vendor.upper() + text = vendor_group.text.strip() add_item(items, (vendor,), text) for device_group in vendor_group.DEVICES: @@ -286,8 +291,8 @@ def sdio_classes(p): items = {} for klass_group in p.CLASSES: - klass = klass_group.KLASS.klass.upper() - text = klass_group.KLASS.text.strip() + klass = klass_group.klass.upper() + text = klass_group.text.strip() add_item(items, klass, text) with open('20-sdio-classes.hwdb', 'wt') as out: diff --git a/hwdb/ma-large.txt b/hwdb/ma-large.txt index 044e9db96..17e588fb7 100644 --- a/hwdb/ma-large.txt +++ b/hwdb/ma-large.txt @@ -3629,12 +3629,6 @@ C05E6F (base 16) V. Stonkaus firma Kodinis Raktas Vilnius 08303 LT -6C-D1-46 (hex) Smartek d.o.o. -6CD146 (base 16) Smartek d.o.o. - B.J.Jelacica 22c - Cakovec Croatia 40000 - US - E0-C2-B7 (hex) Masimo Corporation E0C2B7 (base 16) Masimo Corporation 40 Parker @@ -3767,12 +3761,6 @@ A42C08 (base 16) Masterwork Automodules Orange California 92869 US -80-7B-1E (hex) Corsair Components -807B1E (base 16) Corsair Components - 46221 Landing Parkway - Fremont CA 94538 - US - A0-E2-5A (hex) Amicus SK, s.r.o. A0E25A (base 16) Amicus SK, s.r.o. Koreszkova 9 @@ -9179,12 +9167,6 @@ A8CE90 (base 16) CVC Largo Florida 33773 US -00-1F-53 (hex) GEMAC Gesellschaft für Mikroelektronikanwendung Chemnitz mbH -001F53 (base 16) GEMAC Gesellschaft für Mikroelektronikanwendung Chemnitz mbH - Zwickauer Straße 227 - Chemnitz Sachsen 09116 - DE - 00-1F-4E (hex) ConMed Linvatec 001F4E (base 16) ConMed Linvatec 11311 Concept Blvd. @@ -9197,12 +9179,6 @@ A8CE90 (base 16) CVC Markham Ontario L3R3S1 CA -00-1F-47 (hex) MCS Logic Inc. -001F47 (base 16) MCS Logic Inc. - 6F. Samho Center B Bldg., 275-6 - Seoul 137-941 - KR - 00-1F-D2 (hex) COMMTECH TECHNOLOGY MACAO COMMERCIAL OFFSHORE LTD. 001FD2 (base 16) COMMTECH TECHNOLOGY MACAO COMMERCIAL OFFSHORE LTD. 31,TAI YIP STREET, 7/F KWUN TONG, @@ -21989,12 +21965,6 @@ D059E4 (base 16) Samsung Electronics Co.,Ltd Gumi Gyeongbuk 730-350 KR -00-1D-20 (hex) Comtrend Corporation -001D20 (base 16) Comtrend Corporation - 3F-1 10 LANE 609 CHUNG HSIN ROAD, SEC 5, SAN CHUNG CITY, TAIPEI TAIWAN 241 - TAIPEI 241 - TW - 14-0C-76 (hex) FREEBOX SAS 140C76 (base 16) FREEBOX SAS 16 rue de la Ville l'Eveque @@ -23669,18 +23639,6 @@ A8A648 (base 16) Qingdao Hisense Communications Co.,Ltd. London NW12AA GB -04-9F-81 (hex) NetScout Systems, Inc. -049F81 (base 16) NetScout Systems, Inc. - 310 Littleton Road - Westford MA 01886 - US - -00-80-8C (hex) NetScout Systems, Inc. -00808C (base 16) NetScout Systems, Inc. - 310 Littleton Road - Westford MA 01886 - US - C4-F5-A5 (hex) Kumalift Co., Ltd. C4F5A5 (base 16) Kumalift Co., Ltd. 7-2-6 Saito-Asagi @@ -27005,12 +26963,6 @@ D4E6B7 (base 16) Samsung Electronics Co.,Ltd anyang Gyeonggi-do 14042 KR -88-B3-62 (hex) Nokia Shanghai Bell Co. Ltd.) -88B362 (base 16) Nokia Shanghai Bell Co. Ltd.) - No.388 Ning Qiao Road,Jin Qiao Pudong Shanghai 201206,P.R.China - Shanghai Pudong 201206 - CN - 1C-A0-B8 (hex) Hon Hai Precision Ind. Co., Ltd. 1CA0B8 (base 16) Hon Hai Precision Ind. Co., Ltd. GuangDongShenZhen @@ -28889,18 +28841,6 @@ F84DFC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd. 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 @@ -29003,12 +28943,6 @@ D49234 (base 16) NEC Corporation 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 @@ -31211,16 +31145,22 @@ ACA46E (base 16) SHENZHEN GONGJIN ELECTRONICS CO.,LT Wusha,Chang'An DongGuan City,Guangdong, 523860 CN +5C-5B-35 (hex) Mist Systems, Inc. +5C5B35 (base 16) Mist Systems, Inc. + 1601 South De Anza Blvd, Suite 248 + Cupertino CA 95014 + US + DC-B0-82 (hex) Nokia DCB082 (base 16) Nokia 600 March Road Kanata Ontario K2K 2E6 CA -5C-5B-35 (hex) Mist Systems, Inc. -5C5B35 (base 16) Mist Systems, Inc. - 1601 South De Anza Blvd, Suite 248 - Cupertino CA 95014 +D0-19-6A (hex) Ciena Corporation +D0196A (base 16) Ciena Corporation + 7035 Ridge Road + Hanover MD 21076 US 84-FD-D1 (hex) Intel Corporate @@ -31229,11 +31169,11 @@ DCB082 (base 16) Nokia Kulim Kedah 09000 MY -D0-19-6A (hex) Ciena Corporation -D0196A (base 16) Ciena Corporation - 7035 Ridge Road - Hanover MD 21076 - US +CC-88-26 (hex) LG Innotek +CC8826 (base 16) LG Innotek + 26, Hanamsandan 5beon-ro + Gwangju Gwangsan-gu 506-731 + KR D4-35-1D (hex) Technicolor D4351D (base 16) Technicolor @@ -31247,6 +31187,288 @@ D4351D (base 16) Technicolor Thalwil 8800 CH +84-8B-CD (hex) IEEE Registration Authority +848BCD (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +6C-AB-05 (hex) Cisco Systems, Inc +6CAB05 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +50-AF-4D (hex) zte corporation +50AF4D (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +00-1F-53 (hex) GEMAC Chemnitz GmbH +001F53 (base 16) GEMAC Chemnitz GmbH + Zwickauer Straße 227 + Chemnitz Sachsen 09116 + DE + +A4-A1-79 (hex) Nanjing dianyan electric power automation co. LTD +A4A179 (base 16) Nanjing dianyan electric power automation co. LTD + No. 29, liuzhou north road, pukou district + Nanjing Jiangsu 210031 + CN + +08-7E-64 (hex) Technicolor CH USA Inc. +087E64 (base 16) Technicolor CH USA Inc. + 5030 Sugarloaf Parkway Bldg 6 + Lawrenceville GA 30044 + US + +14-84-30 (hex) MITAC COMPUTING TECHNOLOGY CORPORATION +148430 (base 16) MITAC COMPUTING TECHNOLOGY CORPORATION + 3F., NO.1, R&D ROAD 2, HSINCHU SCIENCE PARK + HSINCHU 30076 + TW + +68-7D-6B (hex) Samsung Electronics Co.,Ltd +687D6B (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +7C-89-56 (hex) Samsung Electronics Co.,Ltd +7C8956 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +60-95-CE (hex) IEEE Registration Authority +6095CE (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +D8-CA-06 (hex) Titan DataCenters France +D8CA06 (base 16) Titan DataCenters France + E.SpacePark 45 Allee des ormes + mougins 06250 + FR + +C8-A7-76 (hex) HUAWEI TECHNOLOGIES CO.,LTD +C8A776 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +90-78-B2 (hex) Xiaomi Communications Co Ltd +9078B2 (base 16) Xiaomi Communications Co Ltd + The Rainbow City of China Resources + NO.68, Qinghe Middle Street Haidian District, Beijing 100085 + CN + +78-C3-13 (hex) China Mobile Group Device Co.,Ltd. +78C313 (base 16) China Mobile Group Device Co.,Ltd. + 32 Xuanwumen West Street,Xicheng District + Beijing 100053 + CN + +B8-91-C9 (hex) Handreamnet +B891C9 (base 16) Handreamnet + #1203 Ace High-end Tower II, 61, Digital-ro 26-gil, Guro-Gu + Seoul 08389 + KR + +A8-49-4D (hex) HUAWEI TECHNOLOGIES CO.,LTD +A8494D (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +FC-BD-67 (hex) Arista Networks +FCBD67 (base 16) Arista Networks + 5453 Great America Parkway + Santa Clara CA 95054 + US + +14-A2-A0 (hex) Cisco Systems, Inc +14A2A0 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +6C-D1-46 (hex) FRAMOS GmbH +6CD146 (base 16) FRAMOS GmbH + Mehlbeerenstr. 2 + Taufkirchen 82024 + DE + +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 + +C4-8A-5A (hex) JFCONTROL +C48A5A (base 16) JFCONTROL + 1449-37 Seoburo + Suwon Gyunggi-do 16643 + KR + +88-B3-62 (hex) Nokia Shanghai Bell Co., Ltd. +88B362 (base 16) Nokia Shanghai Bell Co., Ltd. + No.388 Ning Qiao Road,Jin Qiao Pudong Shanghai 201206,P.R.China + Shanghai Pudong 201206 + CN + +DC-A6-32 (hex) Raspberry Pi Trading Ltd +DCA632 (base 16) Raspberry Pi Trading Ltd + Maurice Wilkes Building, Cowley Road + Cambridge CB4 0DS + GB + +7C-EC-9B (hex) Fuzhou Teraway Information Technology Co.,Ltd +7CEC9B (base 16) Fuzhou Teraway Information Technology Co.,Ltd + 2F, Building 5#, No. 59, Yangqi Road, Cangshan District + Fuzhou Fujian 350000 + CN + +80-7B-1E (hex) Corsair Memory, Inc. +807B1E (base 16) Corsair Memory, Inc. + 47100 Bayside Parkway + Fremont CA 94538 + US + +FC-BC-D1 (hex) HUAWEI TECHNOLOGIES CO.,LTD +FCBCD1 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +4C-E9-E4 (hex) New H3C Technologies Co., Ltd +4CE9E4 (base 16) New H3C Technologies Co., Ltd + 466 Changhe Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +94-0B-19 (hex) HUAWEI TECHNOLOGIES CO.,LTD +940B19 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +04-88-5F (hex) HUAWEI TECHNOLOGIES CO.,LTD +04885F (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +00-1F-47 (hex) MCS Logic Inc. +001F47 (base 16) MCS Logic Inc. + 6F. Samho Center B Bldg., 275-6 + Seoul 137-941 + KR + +70-04-33 (hex) California Things Inc. +700433 (base 16) California Things Inc. + 650 main st + redwood city CA 94063 + US + +78-CC-2B (hex) SINEWY TECHNOLOGY CO., LTD +78CC2B (base 16) SINEWY TECHNOLOGY CO., LTD + 2F., No.179, Dongmin Rd. + Toufen City, Miaoli County 351 + TW + +C8-0D-32 (hex) Holoplot GmbH +C80D32 (base 16) Holoplot GmbH + Ringbahnstr. 12, Hof A2 + Berlin 12099 + DE + +38-94-ED (hex) NETGEAR +3894ED (base 16) NETGEAR + 350 East Plumeria Drive + San Jose CA 95134 + US + +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 + +C4-C6-03 (hex) Cisco Systems, Inc +C4C603 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +60-29-D5 (hex) DAVOLINK Inc. +6029D5 (base 16) DAVOLINK Inc. + 112, Beolmal-ro + Dongan-gu, Anyang-si Gyeonggi-do 14057 + KR + +5C-87-9C (hex) Intel Corporate +5C879C (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +50-E0-85 (hex) Intel Corporate +50E085 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +00-1D-20 (hex) Comtrend Corporation +001D20 (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +00-80-8C (hex) NetAlly +00808C (base 16) NetAlly + 310 Littleton Road + Westford MA 01886 + US + +04-9F-81 (hex) NetAlly +049F81 (base 16) NetAlly + 310 Littleton Road + Westford MA 01886 + US + +1C-64-99 (hex) Comtrend Corporation +1C6499 (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +DC-54-D7 (hex) Amazon Technologies Inc. +DC54D7 (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +6C-5E-3B (hex) Cisco Systems, Inc +6C5E3B (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +7C-94-2A (hex) HUAWEI TECHNOLOGIES CO.,LTD +7C942A (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + 0C-6F-9C (hex) Shaw Communications Inc. 0C6F9C (base 16) Shaw Communications Inc. Suite 900, 630 3rd Avenue S.W. @@ -51905,12 +52127,6 @@ D0D286 (base 16) Beckman Coulter K.K. UNITED KINGDOM GB -00-40-51 (hex) GRACILIS, INC. -004051 (base 16) GRACILIS, INC. - 623 PALACE STREET - AURORA IL 60506 - US - 00-40-64 (hex) KLA INSTRUMENTS CORPORATION 004064 (base 16) KLA INSTRUMENTS CORPORATION 160 RIO ROBLES @@ -53228,12 +53444,6 @@ E09971 (base 16) Samsung Electronics Co.,Ltd Hsinchu TW -F8-8E-85 (hex) Comtrend Corporation -F88E85 (base 16) Comtrend Corporation - 3F-1, NO. 10, LANE 609, - NEW TAIPEI CITY 24159 - TW - 30-0D-43 (hex) Microsoft Mobile Oy 300D43 (base 16) Microsoft Mobile Oy Keilalahdentie 2-4 @@ -54644,12 +54854,6 @@ B0F893 (base 16) Shanghai MXCHIP Information Technology Co., Ltd. Shanghai 200333 CN -00-C0-17 (hex) NetScout Systems, Inc. -00C017 (base 16) NetScout Systems, Inc. - 310 Littleton Road - Westford MA 01886 - US - D4-9B-5C (hex) Chongqing Miedu Technology Co., Ltd. D49B5C (base 16) Chongqing Miedu Technology Co., Ltd. 7-602 No.118 DaPing Main Street Yuzhong District @@ -57203,6 +57407,1416 @@ C43306 (base 16) China Mobile Group Device Co.,Ltd. Sunnyvale CA 94089 US +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 + +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 + +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 + +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 + +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 + +04-F1-28 (hex) HMD Global Oy +04F128 (base 16) HMD Global Oy + Bertel Jungin aukio 9 + Espoo 02600 + FI + +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 + +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 + +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 + +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 + +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 + +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 + +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 + +FC-D2-B6 (hex) IEEE Registration Authority +FCD2B6 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +60-61-DF (hex) Z-meta Research LLC +6061DF (base 16) Z-meta Research LLC + 8365 Quay Drive + Arvada CO 80003 + 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 + +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 + +20-2A-C5 (hex) Petite-En +202AC5 (base 16) Petite-En + 1, Gwanak-ro, Gwanak-gu + Seoul 08826 + KR + +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 + +F4-79-60 (hex) HUAWEI TECHNOLOGIES CO.,LTD +F47960 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +20-65-8E (hex) HUAWEI TECHNOLOGIES CO.,LTD +20658E (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +08-A6-BC (hex) Amazon Technologies Inc. +08A6BC (base 16) Amazon Technologies Inc. + P.O Box 8102 + Reno NV 89507 + US + +EC-AD-E0 (hex) D-Link International +ECADE0 (base 16) D-Link International + 1 Internal Business Park, #03-12,The Synergy, Singapore + Singapore Singapore 609917 + SG + +F0-B9-68 (hex) ITEL MOBILE LIMITED +F0B968 (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 + +98-8B-0A (hex) Hangzhou Hikvision Digital Technology Co.,Ltd. +988B0A (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd. + No.555 Qianmo Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +1C-BF-CE (hex) Shenzhen Century Xinyang Technology Co., Ltd +1CBFCE (base 16) Shenzhen Century Xinyang Technology Co., Ltd + 3F, North Building, Bantian High-tech industrial Zone, No. 2 of Bell Road + Shenzhen Guangdong 518129 + CN + +F8-30-02 (hex) Texas Instruments +F83002 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +B0-2A-1F (hex) Wingtech Group (HongKong)Limited +B02A1F (base 16) Wingtech Group (HongKong)Limited + FLAT/RM 1903 19/F PODIUM PLAZA 5HANOI ROAD TSIM SHA TSUI + Hong Kong Hong Kong 999077 + HK + +1C-B3-E9 (hex) Shenzhen Zhongke United Communication Technology +1CB3E9 (base 16) Shenzhen Zhongke United Communication Technology + 6C jiajiahao commercial building, Shennan avenue + Shenzhen Guangdong 518000 + CN + +34-E1-D1 (hex) IEEE Registration Authority +34E1D1 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +DC-FB-48 (hex) Intel Corporate +DCFB48 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +6C-40-C6 (hex) Nimbus Data, Inc. +6C40C6 (base 16) Nimbus Data, Inc. + 5151 California Ave, Ste 100 + Irvine CA 92617 + US + +A8-DB-03 (hex) SAMSUNG ELECTRO-MECHANICS(THAILAND) +A8DB03 (base 16) SAMSUNG ELECTRO-MECHANICS(THAILAND) + 93Moo5T. Bangsamak SEMTHAI, WELLGROW INDUSTRIAL ESTATE + Bangpakong Chachoengsao 24180 + TH + +44-A6-1E (hex) INGRAM MICRO SERVICES +44A61E (base 16) INGRAM MICRO SERVICES + 100 CHEMIN DE BAILLOT + MONTAUBAN 82000 + FR + +8C-0F-A0 (hex) di-soric GmbH & Co. KG +8C0FA0 (base 16) di-soric GmbH & Co. KG + Steinbeisstrasse 6 + Urbach 73660 + DE + +90-78-41 (hex) Intel Corporate +907841 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +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 + +CC-37-AB (hex) Edgecore Networks Corporation +CC37AB (base 16) Edgecore Networks Corporation + 1 Creation Road 3. + Hsinchu Hsinchu 30077 + TW + +24-79-F3 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD +2479F3 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD + NO.18 HAIBIN ROAD, + DONG GUAN GUANG DONG 523860 + CN + +20-58-69 (hex) Ruckus Wireless +205869 (base 16) Ruckus Wireless + 350 West Java Drive + Sunnyvale CA 94089 + US + +60-D2-DD (hex) Shenzhen Baitong Putian Technology Co.,Ltd. +60D2DD (base 16) Shenzhen Baitong Putian Technology Co.,Ltd. + 501,5/F,Building 1,No.2,Lianwei Street,Hualian Community,Longhua Street Longhua District + Shenzhen Guangdong 518109 + CN + +FC-AB-90 (hex) HUAWEI TECHNOLOGIES CO.,LTD +FCAB90 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +20-DA-22 (hex) HUAWEI TECHNOLOGIES CO.,LTD +20DA22 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +88-F8-72 (hex) HUAWEI TECHNOLOGIES CO.,LTD +88F872 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +70-F2-20 (hex) Actiontec Electronics, Inc +70F220 (base 16) Actiontec Electronics, Inc + 3301 Olcott St. + Santa Clara CA 95054 + US + +00-24-7B (hex) Actiontec Electronics, Inc +00247B (base 16) Actiontec Electronics, Inc + 3301 Olcott St. + Santa Clara CA 95054 + US + +A8-39-44 (hex) Actiontec Electronics, Inc +A83944 (base 16) Actiontec Electronics, Inc + 301 Olcott St + Santa Clara CA 95054 + US + +88-E6-4B (hex) Juniper Networks +88E64B (base 16) Juniper Networks + 1133 Innovation Way + Sunnyvale CA 94089 + US + +48-D8-75 (hex) China TransInfo Technology Co., Ltd +48D875 (base 16) China TransInfo Technology Co., Ltd + Qianfang Building, Phase I, Zhongguancun Software Park, 8 Wangxi Road, Haidian District + Beijing 100085 + CN + +CC-A1-2B (hex) TCL King Electrical Appliances (Huizhou) Co., Ltd +CCA12B (base 16) TCL King Electrical Appliances (Huizhou) Co., Ltd + 10F, TCL Multimedia Building, TCL International E City, No.1001 Zhongshanyuan Rd., Nanshan District + Shenzhen Guangdong 518052 + CN + +4C-BC-48 (hex) Cisco Systems, Inc +4CBC48 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +D4-6A-35 (hex) Cisco Systems, Inc +D46A35 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +E4-F3-E8 (hex) Shenzhen SuperElectron Technology Co.,Ltd. +E4F3E8 (base 16) Shenzhen SuperElectron Technology Co.,Ltd. + 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city + Shenzhen Guangdong 518000 + CN + +B0-30-55 (hex) China Mobile IOT Company Limited +B03055 (base 16) China Mobile IOT Company Limited + NO.8 Yu Ma Road, NanAn Area + Chongqing Chongqing 401336 + CN + +E8-D0-FC (hex) Liteon Technology Corporation +E8D0FC (base 16) Liteon Technology Corporation + 4F, 90, Chien 1 Road + New Taipei City Taiwan 23585 + TW + +C0-9F-E1 (hex) zte corporation +C09FE1 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +AC-00-D0 (hex) zte corporation +AC00D0 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +10-DC-4A (hex) Fiberhome Telecommunication Technologies Co.,LTD +10DC4A (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +44-4B-7E (hex) Fiberhome Telecommunication Technologies Co.,LTD +444B7E (base 16) Fiberhome Telecommunication Technologies Co.,LTD + No.5 DongXin Road + Wuhan Hubei 430074 + CN + +84-C7-8F (hex) STORDIS GmbH +84C78F (base 16) STORDIS GmbH + Rosenwiesstr. 17 + Stuttgart 70567 + DE + +78-2C-29 (hex) New H3C Technologies Co., Ltd +782C29 (base 16) New H3C Technologies Co., Ltd + 466 Changhe Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +98-B8-BA (hex) LG Electronics (Mobile Communications) +98B8BA (base 16) LG Electronics (Mobile Communications) + 60-39, Gasan-dong, Geumcheon-gu + Seoul 153-801 + KR + +D4-9D-C0 (hex) Samsung Electronics Co.,Ltd +D49DC0 (base 16) Samsung Electronics Co.,Ltd + 129, Samsung-ro, Youngtongl-Gu + Suwon Gyeonggi-Do 16677 + KR + +D4-D2-52 (hex) Intel Corporate +D4D252 (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +08-3A-2F (hex) Guangzhou Juan Intelligent Tech Joint Stock Co.,Ltd +083A2F (base 16) Guangzhou Juan Intelligent Tech Joint Stock Co.,Ltd + NO.9, street 3, HengLing industrial zone, Tangdong, tianhe district + Guangzhou Guangdong CN 510000 + CN + +88-EF-16 (hex) ARRIS Group, Inc. +88EF16 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +FC-EA-50 (hex) Integrated Device Technology (Malaysia) Sdn. Bhd. +FCEA50 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd. + Phase 3, Bayan Lepas FIZ + Bayan Lepas Penang 11900 + MY + +44-65-7F (hex) Calix Inc. +44657F (base 16) Calix Inc. + 2777 Orchard Parkway + San Jose CA 95134 + US + +F0-A9-68 (hex) Antailiye Technology Co.,Ltd +F0A968 (base 16) Antailiye Technology Co.,Ltd + 7/F,Zhengjiyuan Buiding,2 Road,Qianjing, Xixiang, Baoan District,Shenzhen + SHEN ZHEN GUANGDONG 518000 + CN + +44-13-D0 (hex) zte corporation +4413D0 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + C0-D9-F7 (hex) ShanDong Domor Intelligent S&T CO.,Ltd C0D9F7 (base 16) ShanDong Domor Intelligent S&T CO.,Ltd Jining high-tech zone base of production,education & research @@ -60482,9 +62096,6 @@ E4D124 (base 16) Mojo Networks, Inc. Chongqing Chongqing 401332 CN -CC-3A-DF (hex) Private -CC3ADF (base 16) Private - B4-CE-FE (hex) James Czekaj B4CEFE (base 16) James Czekaj 41716 Waterfall Rd @@ -60599,12 +62210,6 @@ C46E7B (base 16) SHENZHEN RF-LINK TECHNOLOGY CO.,LTD. Cambridge CB24 9ZR GB -44-65-7F (hex) Calix Inc. -44657F (base 16) Calix Inc. - 1035 North McDowell Boulevard - Petaluma MN 94954 - US - 00-1A-31 (hex) SCAN COIN AB 001A31 (base 16) SCAN COIN AB Jagershillgatan 26 @@ -61175,1403 +62780,368 @@ C07878 (base 16) FLEXTRONICS MANUFACTURING(ZHUHAI)CO.,LTD. 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 +98-0D-67 (hex) Zyxel Communications Corporation +980D67 (base 16) Zyxel Communications Corporation + No. 6 Innovation Road II, Science Park + Hsichu Taiwan 300 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 +00-40-51 (hex) Garbee and Garbee +004051 (base 16) Garbee and Garbee + 4390 Darr Circle + Colorado Springs CO 80908 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. +FC-49-2D (hex) Amazon Technologies Inc. +FC492D (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 +5C-FA-FB (hex) Acubit +5CFAFB (base 16) Acubit + Afred Nobels Vej 21A + Aalborg Ø 9220 + DK -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 - -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 +1C-82-59 (hex) IEEE Registration Authority +1C8259 (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 +74-38-B7 (hex) CANON INC. +7438B7 (base 16) CANON INC. + 30-2 Shimomaruko 3-chome + Ohta-ku Tokyo 146-8501 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 - -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 +B8-A4-4F (hex) Axis Communications AB +B8A44F (base 16) Axis Communications AB + Emdalavägen 14 + LUND 22369 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 +D0-57-94 (hex) Sagemcom Broadband SAS +D05794 (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR -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 +8C-E5-C0 (hex) Samsung Electronics Co.,Ltd +8CE5C0 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +F0-8A-76 (hex) Samsung Electronics Co.,Ltd +F08A76 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +00-FA-21 (hex) Samsung Electronics Co.,Ltd +00FA21 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +14-49-BC (hex) DrayTek Corp. +1449BC (base 16) DrayTek Corp. + No. 26, Fushing Rd., Hukou, Hsinchu Industrial Park, + Hsinchu county 30352 + TW + +28-99-C7 (hex) LINDSAY BROADBAND INC +2899C7 (base 16) LINDSAY BROADBAND INC + 2035 2 FISHER DRIVE + PETERBOROUGH Ontario K9J 6X6 + CA + +BC-7F-A4 (hex) Xiaomi Communications Co Ltd +BC7FA4 (base 16) Xiaomi Communications Co Ltd + The Rainbow City of China Resources + NO.68, Qinghe Middle Street Haidian District, Beijing 100085 CN -64-9D-99 (hex) FS COM INC -649D99 (base 16) FS COM INC - 380 Centerpoint Blvd New Castle - New Castle DE 19720 - US +AC-42-28 (hex) Parta Networks +AC4228 (base 16) Parta Networks + Teknopark Izmir No:38 + URLA IZMIR 35433 + TR -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. +8C-04-BA (hex) Dell Inc. +8C04BA (base 16) Dell Inc. One Dell Way Round Rock TX 78682 US +CC-64-A6 (hex) HUAWEI TECHNOLOGIES CO.,LTD +CC64A6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +18-CF-24 (hex) HUAWEI TECHNOLOGIES CO.,LTD +18CF24 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +08-4F-0A (hex) HUAWEI TECHNOLOGIES CO.,LTD +084F0A (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +50-F8-A5 (hex) eWBM Co., Ltd. +50F8A5 (base 16) eWBM Co., Ltd. + 14F, 9, Teheran-ro 20-gil + Gangnam-gu, Seoul 06236 + KR + +C8-D6-9D (hex) Arab International Optronics +C8D69D (base 16) Arab International Optronics + El Salam St. + El Salam City Cairo 11371 + EG + +BC-97-40 (hex) IEEE Registration Authority +BC9740 (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + 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 + 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 + 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 +40-2B-50 (hex) ARRIS Group, Inc. +402B50 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US -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 +AC-DB-48 (hex) ARRIS Group, Inc. +ACDB48 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +0C-B7-71 (hex) ARRIS Group, Inc. +0CB771 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +6C-29-90 (hex) WiZ Connected Lighting Company Limited +6C2990 (base 16) WiZ Connected Lighting Company Limited + Room 3805, 148 Electric Road + Hong Kong 0000 0000 + HK + +24-16-6D (hex) HUAWEI TECHNOLOGIES CO.,LTD +24166D (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 CN -48-F1-7F (hex) Intel Corporate -48F17F (base 16) Intel Corporate - Lot 8, Jalan Hi-Tech 2/3 - Kulim Kedah 09000 - MY +98-35-ED (hex) HUAWEI TECHNOLOGIES CO.,LTD +9835ED (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN -10-9C-70 (hex) Prusa Research s.r.o. -109C70 (base 16) Prusa Research s.r.o. - Partyzanska 188/7a - Prague 17000 - CZ +70-C7-F2 (hex) HUAWEI TECHNOLOGIES CO.,LTD +70C7F2 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN -8C-44-4F (hex) HUMAX Co., Ltd. -8C444F (base 16) HUMAX Co., Ltd. +8C-FD-18 (hex) HUAWEI TECHNOLOGIES CO.,LTD +8CFD18 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +C8-5D-38 (hex) HUMAX Co., Ltd. +C85D38 (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 - -04-F1-28 (hex) HMD Global Oy -04F128 (base 16) HMD Global Oy - Bertel Jungin aukio 9 - Espoo 02600 - FI - -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 - -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 - -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 - -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 - -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 - -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 - -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 - -FC-D2-B6 (hex) IEEE Registration Authority -FCD2B6 (base 16) IEEE Registration Authority +D0-C8-57 (hex) IEEE Registration Authority +D0C857 (base 16) IEEE Registration Authority 445 Hoes Lane Piscataway NJ 08554 US -60-61-DF (hex) Z-meta Research LLC -6061DF (base 16) Z-meta Research LLC - 8365 Quay Drive - Arvada CO 80003 +B8-07-56 (hex) Cisco Meraki +B80756 (base 16) Cisco Meraki + 500 Terry A. Francois Blvd + San Francisco 94158 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 - -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 +98-86-5D (hex) Nokia Shanghai Bell Co., Ltd. +98865D (base 16) Nokia Shanghai Bell Co., Ltd. + No.388 Ning Qiao Road,Jin Qiao Pudong Shanghai + Shanghai 201206 CN +D8-D4-E6 (hex) Hytec Inter Co., Ltd. +D8D4E6 (base 16) Hytec Inter Co., Ltd. + 3-28-6 Yoyogi + Shibuya-ku Tokyo 1510053 + JP + 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 - -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 - -F4-79-60 (hex) HUAWEI TECHNOLOGIES CO.,LTD -F47960 (base 16) HUAWEI TECHNOLOGIES CO.,LTD - No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park - Dongguan 523808 +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 -20-65-8E (hex) HUAWEI TECHNOLOGIES CO.,LTD -20658E (base 16) HUAWEI TECHNOLOGIES CO.,LTD - No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park - Dongguan 523808 - CN - -08-A6-BC (hex) Amazon Technologies Inc. -08A6BC (base 16) Amazon Technologies Inc. - P.O Box 8102 - Reno NV 89507 +2C-4F-52 (hex) Cisco Systems, Inc +2C4F52 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 US -EC-AD-E0 (hex) D-Link International -ECADE0 (base 16) D-Link International - 1 Internal Business Park, #03-12,The Synergy, Singapore - Singapore Singapore 609917 - SG - -F0-B9-68 (hex) ITEL MOBILE LIMITED -F0B968 (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 - -98-8B-0A (hex) Hangzhou Hikvision Digital Technology Co.,Ltd. -988B0A (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd. - No.555 Qianmo Road, Binjiang District - Hangzhou Zhejiang 310052 +60-3A-7C (hex) TP-LINK TECHNOLOGIES CO.,LTD. +603A7C (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 -1C-BF-CE (hex) Shenzhen Century Xinyang Technology Co., Ltd -1CBFCE (base 16) Shenzhen Century Xinyang Technology Co., Ltd - 3F, North Building, Bantian High-tech industrial Zone, No. 2 of Bell Road - Shenzhen Guangdong 518129 +7C-B5-9B (hex) TP-LINK TECHNOLOGIES CO.,LTD. +7CB59B (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 -F8-30-02 (hex) Texas Instruments -F83002 (base 16) Texas Instruments +F8-E7-A0 (hex) vivo Mobile Communication Co., Ltd. +F8E7A0 (base 16) vivo Mobile Communication Co., Ltd. + #283,BBK Road + Wusha,Chang'An DongGuan City,Guangdong, 523860 + CN + +2C-FF-EE (hex) vivo Mobile Communication Co., Ltd. +2CFFEE (base 16) vivo Mobile Communication Co., Ltd. + #283,BBK Road + Wusha,Chang'An DongGuan City,Guangdong, 523860 + CN + +CC-3A-DF (hex) Private +CC3ADF (base 16) Private + +24-EE-9A (hex) Intel Corporate +24EE9A (base 16) Intel Corporate + Lot 8, Jalan Hi-Tech 2/3 + Kulim Kedah 09000 + MY + +50-97-44 (hex) Integrated Device Technology (Malaysia) Sdn. Bhd. +509744 (base 16) Integrated Device Technology (Malaysia) Sdn. Bhd. + Phase 3, Bayan Lepas FIZ + Bayan Lepas Penang 11900 + MY + +38-1A-52 (hex) Seiko Epson Corporation +381A52 (base 16) Seiko Epson Corporation + 2070 Kotobuki Koaka + Matsumoto-shi Nagano-ken 399-8702 + JP + +88-9F-AA (hex) Hella Gutmann Solutions GmbH +889FAA (base 16) Hella Gutmann Solutions GmbH + Am Krebsbach 2 + Ihringen Baden Württemberg 79241 + DE + +F8-8E-85 (hex) Comtrend Corporation +F88E85 (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +00-C0-17 (hex) NetAlly +00C017 (base 16) NetAlly + 310 Littleton Road + Westford MA 01886 + US + +3C-B7-4B (hex) Technicolor CH USA Inc. +3CB74B (base 16) Technicolor CH USA Inc. + 5030 Sugarloaf Parkway Bldg 6 + Lawrenceville GA 30044 + US + +14-42-FC (hex) Texas Instruments +1442FC (base 16) Texas Instruments 12500 TI Blvd Dallas TX 75243 US -B0-2A-1F (hex) Wingtech Group (HongKong)Limited -B02A1F (base 16) Wingtech Group (HongKong)Limited - FLAT/RM 1903 19/F PODIUM PLAZA 5HANOI ROAD TSIM SHA TSUI - Hong Kong Hong Kong 999077 - HK - -1C-B3-E9 (hex) Shenzhen Zhongke United Communication Technology -1CB3E9 (base 16) Shenzhen Zhongke United Communication Technology - 6C jiajiahao commercial building, Shennan avenue - Shenzhen Guangdong 518000 - CN - -34-E1-D1 (hex) IEEE Registration Authority -34E1D1 (base 16) IEEE Registration Authority - 445 Hoes Lane - Piscataway NJ 08554 +F4-33-28 (hex) CIMCON Lighting Inc. +F43328 (base 16) CIMCON Lighting Inc. + 35 Crosby Drive + Bedford MA 01730 US -DC-FB-48 (hex) Intel Corporate -DCFB48 (base 16) Intel Corporate - Lot 8, Jalan Hi-Tech 2/3 - Kulim Kedah 09000 - MY +B0-B5-E8 (hex) Ruroc LTD +B0B5E8 (base 16) Ruroc LTD + Unit 2, Barnett Way, Barnwood Estate + Gloucester GL4 3RT + GB -6C-40-C6 (hex) Nimbus Data, Inc. -6C40C6 (base 16) Nimbus Data, Inc. - 5151 California Ave, Ste 100 - Irvine CA 92617 +04-D5-90 (hex) Fortinet, Inc. +04D590 (base 16) Fortinet, Inc. + 899 Kifer Road + Sunnyvale 94086 US -A8-DB-03 (hex) SAMSUNG ELECTRO-MECHANICS(THAILAND) -A8DB03 (base 16) SAMSUNG ELECTRO-MECHANICS(THAILAND) - 93Moo5T. Bangsamak SEMTHAI, WELLGROW INDUSTRIAL ESTATE - Bangpakong Chachoengsao 24180 - TH - -44-A6-1E (hex) INGRAM MICRO SERVICES -44A61E (base 16) INGRAM MICRO SERVICES - 100 CHEMIN DE BAILLOT - MONTAUBAN 82000 - FR - -8C-0F-A0 (hex) di-soric GmbH & Co. KG -8C0FA0 (base 16) di-soric GmbH & Co. KG - Steinbeisstrasse 6 - Urbach 73660 - DE - -90-78-41 (hex) Intel Corporate -907841 (base 16) Intel Corporate - Lot 8, Jalan Hi-Tech 2/3 - Kulim Kedah 09000 - MY - -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 - -CC-37-AB (hex) Edgecore Networks Corporation -CC37AB (base 16) Edgecore Networks Corporation - 1 Creation Road 3. - Hsinchu Hsinchu 30077 - TW - -24-79-F3 (hex) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD -2479F3 (base 16) GUANGDONG OPPO MOBILE TELECOMMUNICATIONS CORP.,LTD - NO.18 HAIBIN ROAD, - DONG GUAN GUANG DONG 523860 - CN - -20-58-69 (hex) Ruckus Wireless -205869 (base 16) Ruckus Wireless - 350 West Java Drive - Sunnyvale CA 94089 - US - -60-D2-DD (hex) Shenzhen Baitong Putian Technology Co.,Ltd. -60D2DD (base 16) Shenzhen Baitong Putian Technology Co.,Ltd. - 501,5/F,Building 1,No.2,Lianwei Street,Hualian Community,Longhua Street Longhua District - Shenzhen Guangdong 518109 - CN - -FC-AB-90 (hex) HUAWEI TECHNOLOGIES CO.,LTD -FCAB90 (base 16) HUAWEI TECHNOLOGIES CO.,LTD - No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park - Dongguan 523808 - CN - -20-DA-22 (hex) HUAWEI TECHNOLOGIES CO.,LTD -20DA22 (base 16) HUAWEI TECHNOLOGIES CO.,LTD - No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park - Dongguan 523808 - CN - -88-F8-72 (hex) HUAWEI TECHNOLOGIES CO.,LTD -88F872 (base 16) HUAWEI TECHNOLOGIES CO.,LTD - No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park - Dongguan 523808 - CN - -70-F2-20 (hex) Actiontec Electronics, Inc -70F220 (base 16) Actiontec Electronics, Inc - 3301 Olcott St. - Santa Clara CA 95054 - US - -00-24-7B (hex) Actiontec Electronics, Inc -00247B (base 16) Actiontec Electronics, Inc - 3301 Olcott St. - Santa Clara CA 95054 - US - -A8-39-44 (hex) Actiontec Electronics, Inc -A83944 (base 16) Actiontec Electronics, Inc - 301 Olcott St - Santa Clara CA 95054 - US - -88-E6-4B (hex) Juniper Networks -88E64B (base 16) Juniper Networks - 1133 Innovation Way - Sunnyvale CA 94089 - US - -48-D8-75 (hex) China TransInfo Technology Co., Ltd -48D875 (base 16) China TransInfo Technology Co., Ltd - Qianfang Building, Phase I, Zhongguancun Software Park, 8 Wangxi Road, Haidian District - Beijing 100085 - CN - -CC-A1-2B (hex) TCL King Electrical Appliances (Huizhou) Co., Ltd -CCA12B (base 16) TCL King Electrical Appliances (Huizhou) Co., Ltd - 10F, TCL Multimedia Building, TCL International E City, No.1001 Zhongshanyuan Rd., Nanshan District - Shenzhen Guangdong 518052 - CN - -4C-BC-48 (hex) Cisco Systems, Inc -4CBC48 (base 16) Cisco Systems, Inc +5C-5A-C7 (hex) Cisco Systems, Inc +5C5AC7 (base 16) Cisco Systems, Inc 80 West Tasman Drive San Jose CA 94568 US -D4-6A-35 (hex) Cisco Systems, Inc -D46A35 (base 16) Cisco Systems, Inc - 80 West Tasman Drive - San Jose CA 94568 - US - -E4-F3-E8 (hex) Shenzhen SuperElectron Technology Co.,Ltd. -E4F3E8 (base 16) Shenzhen SuperElectron Technology Co.,Ltd. - 1213-1214, haosheng business center, dongbin road, nanshan street, nanshan district, shenzhen city - Shenzhen Guangdong 518000 +1C-B7-96 (hex) HUAWEI TECHNOLOGIES CO.,LTD +1CB796 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 CN -B0-30-55 (hex) China Mobile IOT Company Limited -B03055 (base 16) China Mobile IOT Company Limited - NO.8 Yu Ma Road, NanAn Area - Chongqing Chongqing 401336 +D4-62-EA (hex) HUAWEI TECHNOLOGIES CO.,LTD +D462EA (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 CN -E8-D0-FC (hex) Liteon Technology Corporation -E8D0FC (base 16) Liteon Technology Corporation - 4F, 90, Chien 1 Road - New Taipei City Taiwan 23585 - TW - -C0-9F-E1 (hex) zte corporation -C09FE1 (base 16) zte corporation - 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China - shenzhen guangdong 518057 +38-47-BC (hex) HUAWEI TECHNOLOGIES CO.,LTD +3847BC (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 CN -AC-00-D0 (hex) zte corporation -AC00D0 (base 16) zte corporation - 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China - shenzhen guangdong 518057 - CN - -10-DC-4A (hex) Fiberhome Telecommunication Technologies Co.,LTD -10DC4A (base 16) Fiberhome Telecommunication Technologies Co.,LTD - No.5 DongXin Road - Wuhan Hubei 430074 - CN - -44-4B-7E (hex) Fiberhome Telecommunication Technologies Co.,LTD -444B7E (base 16) Fiberhome Telecommunication Technologies Co.,LTD - No.5 DongXin Road - Wuhan Hubei 430074 - CN - -84-C7-8F (hex) STORDIS GmbH -84C78F (base 16) STORDIS GmbH - Rosenwiesstr. 17 - Stuttgart 70567 - DE - -78-2C-29 (hex) New H3C Technologies Co., Ltd -782C29 (base 16) New H3C Technologies Co., Ltd - 466 Changhe Road, Binjiang District - Hangzhou Zhejiang 310052 - CN - -98-B8-BA (hex) LG Electronics (Mobile Communications) -98B8BA (base 16) LG Electronics (Mobile Communications) - 60-39, Gasan-dong, Geumcheon-gu - Seoul 153-801 - KR - -D4-9D-C0 (hex) Samsung Electronics Co.,Ltd -D49DC0 (base 16) Samsung Electronics Co.,Ltd - 129, Samsung-ro, Youngtongl-Gu - Suwon Gyeonggi-Do 16677 - KR - -D4-D2-52 (hex) Intel Corporate -D4D252 (base 16) Intel Corporate - Lot 8, Jalan Hi-Tech 2/3 - Kulim Kedah 09000 - MY +F8-AD-CB (hex) HMD Global Oy +F8ADCB (base 16) HMD Global Oy + Bertel Jungin aukio 9 + Espoo 02600 + FI 58-46-E1 (hex) Baxter International Inc 5846E1 (base 16) Baxter International Inc @@ -65978,12 +66548,6 @@ D862DB (base 16) Eno Inc. Tokyo 1700013 JP -68-DB-67 (hex) Nantong Coship Electronics Co., Ltd -68DB67 (base 16) Nantong Coship Electronics Co., Ltd - No.188 Xinsheng Road - Nantong Jiangsu 226000 - US - BC-26-1D (hex) HONG KONG TECON TECHNOLOGY BC261D (base 16) HONG KONG TECON TECHNOLOGY ROOM 2802,BLOCK A,SHEN FANG OLAZA @@ -66152,12 +66716,6 @@ C47DCC (base 16) Zebra Technologies Inc Freiburg BW 79111 US -84-C2-E4 (hex) Jiangsu Qinheng Co., Ltd. -84C2E4 (base 16) Jiangsu Qinheng Co., Ltd. - No. 18, Ningshuang Road - Nanjing Jiangsu 210012 - CN - C0-B8-B1 (hex) BitBox Ltd C0B8B1 (base 16) BitBox Ltd Whitney Road @@ -75959,12 +76517,6 @@ A893E6 (base 16) JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LT Walkersville Maryland 21793 US -00-0C-FF (hex) MRO-TEK LIMITED -000CFF (base 16) MRO-TEK LIMITED - 14, 1ST D MAIN ROAD, - BANGALORE KARNATAKA 560032 - IN - 00-0C-ED (hex) Real Digital Media 000CED (base 16) Real Digital Media 485 North Keller Road @@ -77294,12 +77846,6 @@ A893E6 (base 16) JIANGXI JINGGANGSHAN CKING COMMUNICATION TECHNOLOGY CO.,LT Cupertino CA 95014 US -00-08-5D (hex) Aastra -00085D (base 16) Aastra - 1160 Route 22 East - Bridgewater NJ 08807 - US - 00-08-62 (hex) NEC Eluminant Technologies, Inc. 000862 (base 16) NEC Eluminant Technologies, Inc. 14700 Avion Parkway @@ -85826,12 +86372,6 @@ BC282C (base 16) e-Smart Systems Pvt. Ltd Dallas TX 75243 US -D8-E7-2B (hex) NetScout Systems, Inc. -D8E72B (base 16) NetScout Systems, Inc. - 310 Littleton Road - Westford MA 01886 - US - 04-FE-A1 (hex) Fihonest communication co.,Ltd 04FEA1 (base 16) Fihonest communication co.,Ltd Room902,Park road,Zhixing business-building @@ -86630,12 +87170,6 @@ B83A9D (base 16) Alarm.com INCHEON 22830 KR -E0-D9-E3 (hex) Eltex Enterprise Ltd. -E0D9E3 (base 16) Eltex Enterprise Ltd. - Okruzhnaya 29v - Novosibirsk 630020 - RU - 50-98-F3 (hex) Rheem Australia Pty Ltd 5098F3 (base 16) Rheem Australia Pty Ltd 1 Alan Street @@ -90176,12 +90710,6 @@ B8F74A (base 16) RCNTEC Moscow 127018 RU -C8-D1-2A (hex) Comtrend Corporation -C8D12A (base 16) Comtrend Corporation - 3F-1, No. 10, Lane 609, Chung Hsin Road, Sec 5, San Chung Dist. - New Taipei City Taiwan 24159 - TW - B4-F1-DA (hex) LG Electronics (Mobile Communications) B4F1DA (base 16) LG Electronics (Mobile Communications) 60-39, Gasan-dong, Geumcheon-gu @@ -90854,12 +91382,6 @@ D0B214 (base 16) PoeWit Inc Fort Lauderdale FL 33301 US -08-47-D0 (hex) Nokia Shanghai Bell Co. Ltd.) -0847D0 (base 16) Nokia Shanghai Bell Co. Ltd.) - No.388 Ning Qiao Road,Jin Qiao Pudong Shanghai 201206,P.R.China - Shanghai Pudong 201206 - CN - 18-69-DA (hex) China Mobile Group Device Co.,Ltd. 1869DA (base 16) China Mobile Group Device Co.,Ltd. 32 Xuanwumen West Street,Xicheng District @@ -92330,30 +92852,6 @@ C8F742 (base 16) HangZhou Gubei Electronics Technology Co.,Ltd 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 @@ -92750,12 +93248,6 @@ A4CF12 (base 16) Espressif Inc. 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 @@ -92972,12 +93464,6 @@ E8ECA3 (base 16) Dongguan Liesheng Electronic Co.Ltd shenzhen guangdong 518057 CN -20-96-8A (hex) China Mobile (Hangzhou) Information Technology Co., Ltd. -20968A (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd. - No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District - Hangzhou Zhejiang 310000 - CN - F0-D4-F7 (hex) varram system F0D4F7 (base 16) varram system 57, TECHNO 11-RO,YUSEONG-GU, DAEJEON, KOREA @@ -93230,6 +93716,300 @@ D420B0 (base 16) Mist Systems, Inc. Cologne NW 50676 DE +EC-5B-73 (hex) Advanced & Wise Technology Corp. +EC5B73 (base 16) Advanced & Wise Technology Corp. + 5F, No. 3-2, Industry East 9th Road, Hsinchu Science Park, + Hsinchu City Hsinchu 30075 + TW + +60-D2-48 (hex) ARRIS Group, Inc. +60D248 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +C0-89-AB (hex) ARRIS Group, Inc. +C089AB (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +68-6D-BC (hex) Hangzhou Hikvision Digital Technology Co.,Ltd. +686DBC (base 16) Hangzhou Hikvision Digital Technology Co.,Ltd. + No.555 Qianmo Road, Binjiang District + Hangzhou Zhejiang 310052 + CN + +18-AA-CA (hex) Sichuan tianyi kanghe communications co., LTD +18AACA (base 16) Sichuan tianyi kanghe communications co., LTD + No.198, section 1, xueshan avenue, jinyuan town, dayi county, sichuan province + chengdu sichuan 611330 + CN + +D4-F0-57 (hex) Nintendo Co.,Ltd +D4F057 (base 16) Nintendo Co.,Ltd + 11-1 HOKOTATE-CHO KAMITOBA,MINAMI-KU + KYOTO KYOTO 601-8501 + JP + +68-DB-67 (hex) Nantong Coship Electronics Co., Ltd. +68DB67 (base 16) Nantong Coship Electronics Co., Ltd. + No.188 Xinsheng Road + Nantong Jiangsu 226000 + US + +84-C2-E4 (hex) Jiangsu Qinheng Co., Ltd. +84C2E4 (base 16) Jiangsu Qinheng Co., Ltd. + No. 18, Ningshuang Road + Nanjing Jiangsu 210012 + CN + +74-45-CE (hex) CRESYN +7445CE (base 16) CRESYN + 8-22,Jamwon-dong + Seoul Seocho-Gu #137-902 + KR + +C8-EA-F8 (hex) zte corporation +C8EAF8 (base 16) zte corporation + 12/F.,zte R&D building ,kejinan Road,Shenzhen,P.R.China + shenzhen guangdong 518057 + CN + +30-31-7D (hex) Hosiden Corporation +30317D (base 16) Hosiden Corporation + 4-33, Kitakyuhoji 1-chome + Yao Osaka 5810071 + JP + +38-3B-26 (hex) Jiangsu Qinheng Co., Ltd. +383B26 (base 16) Jiangsu Qinheng Co., Ltd. + No. 18, Ningshuang Road + Nanjing Jiangsu 210012 + CN + +BC-98-DF (hex) Motorola Mobility LLC, a Lenovo Company +BC98DF (base 16) Motorola Mobility LLC, a Lenovo Company + 222 West Merchandise Mart Plaza + Chicago IL 60654 + US + +74-EE-2A (hex) SHENZHEN BILIAN ELECTRONIC CO.,LTD +74EE2A (base 16) SHENZHEN BILIAN ELECTRONIC CO.,LTD + NO.268, Fuqian Rd, Jutang community, Guanlan Town, Longhua New district + shenzhen guangdong 518000 + CN + +C4-F7-D5 (hex) Cisco Systems, Inc +C4F7D5 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +54-E0-19 (hex) Ring LLC +54E019 (base 16) Ring LLC + 1523 26th St + Santa Monica CA 90404 + US + +E0-D9-E3 (hex) Eltex Enterprise Ltd. +E0D9E3 (base 16) Eltex Enterprise Ltd. + Okruzhnaya st. 29v + Novosibirsk 630020 + RU + +D8-13-99 (hex) Hui Zhou Gaoshengda Technology Co.,LTD +D81399 (base 16) Hui Zhou Gaoshengda Technology Co.,LTD + No.75,Zhongkai High-Tech Development District,Huizhou + Hui Zhou Guangdong 516006 + CN + +E8-28-C1 (hex) Eltex Enterprise Ltd. +E828C1 (base 16) Eltex Enterprise Ltd. + Okruzhnaya st. 29v + Novosibirsk 630020 + RU + +6C-F1-7E (hex) Zhejiang Uniview Technologies Co.,Ltd. +6CF17E (base 16) Zhejiang Uniview Technologies Co.,Ltd. + No.88,Jiangling Road + Hangzhou Zhejiang,P.R.China 310051 + CN + +00-08-5D (hex) Mitel Corporation +00085D (base 16) Mitel Corporation + 350 Legget Drive + - K2K 2W7 + CA + +18-B6-F7 (hex) NEW POS TECHNOLOGY LIMITED +18B6F7 (base 16) NEW POS TECHNOLOGY LIMITED + AB Unit, 14th Floor,Block A, Financial Technology Building.No. 11 Keyuan Rd + Shenzhen 518057 + CN + +BC-97-E1 (hex) Broadcom Limited +BC97E1 (base 16) Broadcom Limited + 15191 Alton Parkway + Irvine CA 92618 + US + +EC-AA-25 (hex) Samsung Electronics Co.,Ltd +ECAA25 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +18-19-D6 (hex) Samsung Electronics Co.,Ltd +1819D6 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +54-EC-2F (hex) Ruckus Wireless +54EC2F (base 16) Ruckus Wireless + 350 West Java Drive + Sunnyvale CA 94089 + US + +20-F4-78 (hex) Xiaomi Communications Co Ltd +20F478 (base 16) Xiaomi Communications Co Ltd + The Rainbow City of China Resources + NO.68, Qinghe Middle Street Haidian District, Beijing 100085 + CN + +1C-20-DB (hex) HUAWEI TECHNOLOGIES CO.,LTD +1C20DB (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +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 + +08-47-D0 (hex) Nokia Shanghai Bell Co., Ltd. +0847D0 (base 16) Nokia Shanghai Bell Co., Ltd. + No.388 Ning Qiao Road,Jin Qiao Pudong Shanghai 201206,P.R.China + Shanghai Pudong 201206 + 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 + +C8-50-CE (hex) HUAWEI TECHNOLOGIES CO.,LTD +C850CE (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +CC-90-70 (hex) Cisco Systems, Inc +CC9070 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +54-EF-44 (hex) Lumi United Technology Co., Ltd +54EF44 (base 16) Lumi United Technology Co., Ltd + 8th Floor, JinQi Wisdom Valley, No.1 TangLing Road, LinXian Ave, Taoyuan Residential District,Nanshan District + ShenZhen GuangDong 518055 + CN + +48-A7-3C (hex) Sichuan tianyi kanghe communications co., LTD +48A73C (base 16) Sichuan tianyi kanghe communications co., LTD + No.198, section 1, xueshan avenue, jinyuan town, dayi county + chengdu sichuan 611330 + CN + +20-96-8A (hex) China Mobile (Hangzhou) Information Technology Co., Ltd. +20968A (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd. + No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District + Hangzhou Zhejiang 310000 + CN + +9C-99-CD (hex) Voippartners +9C99CD (base 16) Voippartners + Via di Passolombardo 35 + Rome 00133 + IT + +C8-D1-2A (hex) Comtrend Corporation +C8D12A (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +00-0C-FF (hex) MRO-TEK Realty Limited +000CFF (base 16) MRO-TEK Realty Limited + #6, New BEL Road + Chikkamaranahalli Bangalore 560 054 + IN + +D8-E7-2B (hex) NetAlly +D8E72B (base 16) NetAlly + 310 Littleton Road + Westford MA 01886 + US + +00-35-FF (hex) Texas Instruments +0035FF (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +44-59-E3 (hex) HUAWEI TECHNOLOGIES CO.,LTD +4459E3 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +54-BA-D6 (hex) HUAWEI TECHNOLOGIES CO.,LTD +54BAD6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +74-59-09 (hex) HUAWEI TECHNOLOGIES CO.,LTD +745909 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +E4-FD-A1 (hex) HUAWEI TECHNOLOGIES CO.,LTD +E4FDA1 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +68-3F-1E (hex) EFFECT Photonics B.V. +683F1E (base 16) EFFECT Photonics B.V. + Kastanjelaan 400 + Eindhoven Noord-Brabant 5616 LZ + NL + D8-6C-E9 (hex) Sagemcom Broadband SAS D86CE9 (base 16) Sagemcom Broadband SAS 250 route de l'Empereur @@ -96524,12 +97304,6 @@ C034B4 (base 16) Gigastone Corporation Taipei 11492 TW -74-AD-B7 (hex) China Mobile Group Device Co.,Ltd. -74ADB7 (base 16) China Mobile Group Device Co.,Ltd. - 32 Xuanwumen West Street - Beijing 100053 - CN - DC-6F-00 (hex) Livescribe, Inc. DC6F00 (base 16) Livescribe, Inc. 7677 Oakport Street @@ -101285,12 +102059,6 @@ EC3091 (base 16) Cisco Systems, Inc Mamaroneck NY 10543 US -00-25-7E (hex) NEW POS Technology Limited -00257E (base 16) NEW POS Technology Limited - 6FRM, 6F, China Economic Trade Building - Shenzhen Guangdong 518000 - CN - 00-25-72 (hex) Nemo-Q International AB 002572 (base 16) Nemo-Q International AB Box 210 @@ -109646,12 +110414,6 @@ EC3091 (base 16) Cisco Systems, Inc 94537 Orly Cedex FR -00-04-C4 (hex) Allen & Heath Limited -0004C4 (base 16) Allen & Heath Limited - Kernick Industrial Estate, Penryn - Cornwall England TR10 9LU - GB - 00-04-B7 (hex) AMB i.t. Holding 0004B7 (base 16) AMB i.t. Holding Zuiderhoutlaan 4 @@ -115223,12 +115985,6 @@ A80600 (base 16) Samsung Electronics Co.,Ltd Gumi Gyeongbuk 730-350 KR -38-72-C0 (hex) Comtrend Corporation -3872C0 (base 16) Comtrend Corporation - 3F-1,10 Lane 609, Chung Hsin Road, Section 5 - Taipei Hsien 241 - TW - 00-0B-CA (hex) DATAVAN TC 000BCA (base 16) DATAVAN TC 4FL,#120-12,Chung Shan Rd, Sec.3 @@ -116609,12 +117365,6 @@ EC8EAE (base 16) Nagravision SA Enschede NL-7500 NL -00-A0-0E (hex) NetScout Systems, Inc. -00A00E (base 16) NetScout Systems, Inc. - 310 Littleton Road - Westford MA 01886 - US - 1C-33-0E (hex) PernixData 1C330E (base 16) PernixData 1745 Technology Drive, Suite 800 @@ -118331,24 +119081,6 @@ C4D197 (base 16) Ventia Utility Services Köln 51147 DE -CC-BE-59 (hex) Calix Inc. -CCBE59 (base 16) Calix Inc. - 1035 North McDowell Boulevard - Petaluma MN 94954 - US - -EC-4F-82 (hex) Calix Inc. -EC4F82 (base 16) Calix Inc. - 1035 North McDowell Boulevard - Petaluma MN 94954 - US - -00-06-31 (hex) Calix Inc. -000631 (base 16) Calix Inc. - 1035 North McDowell Boulevard - Petaluma MN 94954 - US - B8-D7-AF (hex) Murata Manufacturing Co., Ltd. B8D7AF (base 16) Murata Manufacturing Co., Ltd. 1-10-1, Higashikotari @@ -123626,12 +124358,6 @@ D4AD71 (base 16) Cisco Systems, Inc 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 @@ -124370,6 +125096,267 @@ D44DA4 (base 16) Murata Manufacturing Co., Ltd. Nagaokakyo-shi Kyoto 617-8555 JP +8C-A9-6F (hex) D&M Holdings Inc. +8CA96F (base 16) D&M Holdings Inc. + D&M Building, 2-1 Nisshin-cho + Kawasaki-shi Kanagawa 210-8569 + JP + +14-C0-3E (hex) ARRIS Group, Inc. +14C03E (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +08-ED-ED (hex) Zhejiang Dahua Technology Co., Ltd. +08EDED (base 16) Zhejiang Dahua Technology Co., Ltd. + No.1199,Waterfront Road + Hangzhou Zhejiang 310053 + CN + +AC-F5-E6 (hex) Cisco Systems, Inc +ACF5E6 (base 16) Cisco Systems, Inc + 80 West Tasman Drive + San Jose CA 94568 + US + +20-17-42 (hex) LG Electronics +201742 (base 16) LG Electronics + 222 LG-ro, JINWI-MYEON + Pyeongtaek-si Gyeonggi-do 451-713 + KR + +00-06-31 (hex) Calix Inc. +000631 (base 16) Calix Inc. + 2777 Orchard Parkway + San Jose CA 95134 + US + +EC-4F-82 (hex) Calix Inc. +EC4F82 (base 16) Calix Inc. + 2777 Orchard Parkway + San Jose CA 95134 + US + +CC-BE-59 (hex) Calix Inc. +CCBE59 (base 16) Calix Inc. + 2777 Orchard Parkway + San Jose CA 95134 + US + +24-62-AB (hex) Espressif Inc. +2462AB (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +88-B4-36 (hex) Private +88B436 (base 16) Private + +48-E1-E9 (hex) Chengdu Meross Technology Co., Ltd. +48E1E9 (base 16) Chengdu Meross Technology Co., Ltd. + No. 25, Yizhou Avenue, Gaoxin + Chengdu Sichuan 610000 + CN + +6C-8A-EC (hex) Nantong Coship Electronics Co., Ltd. +6C8AEC (base 16) Nantong Coship Electronics Co., Ltd. + No.188 Xinsheng Road + Nantong Jiangsu 226001 + CN + +88-9E-33 (hex) TCT mobile ltd +889E33 (base 16) TCT mobile ltd + No.86 hechang 7th road, zhongkai, Hi-Tech District + Hui Zhou Guang Dong 516006 + CN + +04-5C-6C (hex) Juniper Networks +045C6C (base 16) Juniper Networks + 1133 Innovation Way + Sunnyvale CA 94089 + US + +04-D9-F5 (hex) ASUSTek COMPUTER INC. +04D9F5 (base 16) ASUSTek COMPUTER INC. + 15,Li-Te Rd., Peitou, Taipei 112, Taiwan + Taipei Taiwan 112 + TW + +50-1B-32 (hex) Taicang T&W Electronics +501B32 (base 16) Taicang T&W Electronics + 89# Jiang Nan RD + Suzhou Jiangsu 215412 + CN + +B4-CF-E0 (hex) Sichuan tianyi kanghe communications co., LTD +B4CFE0 (base 16) Sichuan tianyi kanghe communications co., LTD + No.198, section 1, xueshan avenue, jinyuan town, dayi county + chengdu sichuan 611330 + CN + +88-29-9C (hex) Samsung Electronics Co.,Ltd +88299C (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +48-51-69 (hex) Samsung Electronics Co.,Ltd +485169 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +70-FC-8F (hex) FREEBOX SAS +70FC8F (base 16) FREEBOX SAS + 16 rue de la Ville l'Eveque + PARIS IdF 75008 + FR + +00-25-7E (hex) NEW POS TECHNOLOGY LIMITED +00257E (base 16) NEW POS TECHNOLOGY LIMITED + 6FRM, 6F, China Economic Trade Building + Shenzhen Guangdong 518000 + CN + +74-AD-B7 (hex) China Mobile Group Device Co.,Ltd. +74ADB7 (base 16) China Mobile Group Device Co.,Ltd. + 32 Xuanwumen West Street + Beijing 100053 + CN + +74-34-AE (hex) this is engineering Inc. +7434AE (base 16) this is engineering Inc. + 352, 815 Daewangpangyo-ro, Sujeong-gu + Seongnam-si Gyeonggi-do 13449 + KR + +C4-8F-C1 (hex) DEEPTRACK S.L.U. +C48FC1 (base 16) DEEPTRACK S.L.U. + Avenida de Barajas 32, Parque E. Omega, Edificio A + Alcobendas 28100 + ES + +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 + +28-41-C6 (hex) HUAWEI TECHNOLOGIES CO.,LTD +2841C6 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +F8-A7-63 (hex) Zhejiang Tmall Technology Co., Ltd. +F8A763 (base 16) Zhejiang Tmall Technology Co., Ltd. + Ali Center,No.3331 Keyuan South RD (Shenzhen bay), Nanshan District, Shenzhen Guangdong province + Shenzhen GuangDong 518000 + CN + +14-AD-CA (hex) China Mobile Iot Limited company +14ADCA (base 16) China Mobile Iot Limited company + No. 8 Yangliu North Road, Yubei District, Chongqing, China + Chong Qing Chong Qing 401120 + CN + +38-01-18 (hex) ULVAC,Inc. +380118 (base 16) ULVAC,Inc. + 2500 Hagizono + Chigasaki Kanagawa 253-8543 + JP + +C4-41-1E (hex) Belkin International Inc. +C4411E (base 16) Belkin International Inc. + 12045 East Waterfront Drive + Playa Vista 90094 + US + +00-77-E4 (hex) Nokia +0077E4 (base 16) Nokia + Karaportti 3 + Espoo Finland 02610 + FI + +84-0B-7C (hex) Hitron Technologies. Inc +840B7C (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 + +B4-54-59 (hex) China Mobile (Hangzhou) Information Technology Co., Ltd. +B45459 (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd. + No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District + Hangzhou Zhejiang 310000 + CN + +00-04-C4 (hex) Audiotonix Group Limited +0004C4 (base 16) Audiotonix Group Limited + Unit 10, Silverglade Business Park + Chessington Surrey KT9 2QL + GB + +00-AD-63 (hex) Dedicated Micros Malta LTD +00AD63 (base 16) Dedicated Micros Malta LTD + Blb017, Qasam Industrijali Bulebel + ZEJTUN ZTN 3000 + MT + +B4-DC-09 (hex) Guangzhou Dawei Communication Co.,Ltd +B4DC09 (base 16) Guangzhou Dawei Communication Co.,Ltd + Zone A 906#, International Business Incubator, No.3 Juquan Road, Huangpu District + Guangzhou Guangdong 510660 + CN + +BC-A1-3A (hex) SES-imagotag +BCA13A (base 16) SES-imagotag + St.-Peter-Gürtel 10b + Graz 8010 + AT + +2C-1E-4F (hex) Chengdu Qianli Network Technology Co., Ltd. +2C1E4F (base 16) Chengdu Qianli Network Technology Co., Ltd. + Room 1208, 4 Building, Ideal Center, NO.38 Tianyi Street, Chengdu High-tech Zone + Chengdu Sichuan 610000 + CN + +AC-5D-5C (hex) FN-LINK TECHNOLOGY LIMITED +AC5D5C (base 16) FN-LINK TECHNOLOGY LIMITED + A Building,HuiXin industial park,No 31, YongHe road, Fuyong town, Bao'an District + SHENZHEN GUANGDONG 518100 + CN + +38-72-C0 (hex) Comtrend Corporation +3872C0 (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +00-A0-0E (hex) NetAlly +00A00E (base 16) NetAlly + 310 Littleton Road + Westford MA 01886 + US + +A4-AE-11 (hex) Hon Hai Precision Ind. Co., Ltd. +A4AE11 (base 16) Hon Hai Precision Ind. Co., Ltd. + GuangDongShenZhen + ShenZhen GuangDong 518109 + CN + +B4-52-A9 (hex) Texas Instruments +B452A9 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +E4-54-E8 (hex) Dell Inc. +E454E8 (base 16) Dell Inc. + One Dell Way + Round Rock TX 78682 + US + 2C-39-96 (hex) Sagemcom Broadband SAS 2C3996 (base 16) Sagemcom Broadband SAS 250 route de l'Empereur @@ -126467,12 +127454,6 @@ D855A3 (base 16) zte corporation Shenzhen Guangdong 518054 CN -D8-B6-B7 (hex) Comtrend Corporation -D8B6B7 (base 16) Comtrend Corporation - 3F-1, 10 Lane 609, - New Taipei City , Taiwan 24159 - TW - 04-99-E6 (hex) Shenzhen Yoostar Technology Co., Ltd 0499E6 (base 16) Shenzhen Yoostar Technology Co., Ltd #503,Tower D, HUA HAN Innovation Park, @@ -132050,12 +133031,6 @@ A0593A (base 16) V.D.S. Video Display Systems srl SCANDICCI FI 50018 IT -A8-F9-4B (hex) Eltex Enterprise Ltd. -A8F94B (base 16) Eltex Enterprise Ltd. - Obyedineniya st., 9 - Novosibirsk 630020 - RU - 90-6D-C8 (hex) DLG Automação Industrial Ltda 906DC8 (base 16) DLG Automação Industrial Ltda Rua José Batista Soares, 53 @@ -138752,12 +139727,6 @@ DC3350 (base 16) TechSAT GmbH Moss Landing CA 95039 US -00-0C-6C (hex) Elgato Systems LLC -000C6C (base 16) Elgato Systems LLC - 900 Kearny St #750 - San Francisco CA 94133 - US - 00-0B-88 (hex) Vidisco ltd. 000B88 (base 16) Vidisco ltd. 17 Yechiel Dresner @@ -138944,12 +139913,6 @@ DC3350 (base 16) TechSAT GmbH Shibata Miyagi pref. 989-1695 JP -00-0B-E4 (hex) Hosiden Corporation -000BE4 (base 16) Hosiden Corporation - 4-33 - Yao-city Osaka 581-0071 - JP - 00-0B-D8 (hex) Industrial Scientific Corp. 000BD8 (base 16) Industrial Scientific Corp. 1001 Oakdale Road @@ -142772,12 +143735,6 @@ DC3350 (base 16) TechSAT GmbH GOLDEN CO 80401 US -00-50-C6 (hex) LOOP TELECOMMUNICATION INTERNATIONAL, INC. -0050C6 (base 16) LOOP TELECOMMUNICATION INTERNATIONAL, INC. - 2F, NO. 22, PROSPERITY RD. 2 - HSINCHU - TW - 00-50-49 (hex) Arbor Networks Inc 005049 (base 16) Arbor Networks Inc 6 Omni Way @@ -146690,18 +147647,6 @@ D8C4E9 (base 16) Samsung Electronics Co.,Ltd Hsinchu TW -00-30-DA (hex) Comtrend Corporation -0030DA (base 16) Comtrend Corporation - 3F-1 10 LANE 609 CHUNG - TAIWAN 241 - TW - -64-68-0C (hex) Comtrend Corporation -64680C (base 16) Comtrend Corporation - 3F-1,10 Lane 609, Chung Hsin Road, Section 5 - Taipei Hsien 241 - TW - 00-CF-1C (hex) Communication Machinery Corporation 00CF1C (base 16) Communication Machinery Corporation 1226 ANACAPA @@ -153185,9 +154130,6 @@ B0FC0D (base 16) Amazon Technologies Inc. Reno NV 89507 US -E0-CB-1D (hex) Private -E0CB1D (base 16) Private - 10-7B-A4 (hex) Olive & Dove Co.,Ltd. 107BA4 (base 16) Olive & Dove Co.,Ltd. 803 Polaris bldg., 381, Seongnam-daero, Bundang-gu @@ -153218,9 +154160,6 @@ D49E05 (base 16) zte corporation Nes Ziona 7404996 IL -9C-93-E4 (hex) Private -9C93E4 (base 16) Private - 74-C1-4F (hex) HUAWEI TECHNOLOGIES CO.,LTD 74C14F (base 16) HUAWEI TECHNOLOGIES CO.,LTD No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park @@ -153845,12 +154784,6 @@ A49B4F (base 16) HUAWEI TECHNOLOGIES CO.,LTD SANTA CLARA CA 95052 US -58-C8-76 (hex) China Mobile (Hangzhou) Information Technology Co., Ltd. -58C876 (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd. - No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District - Hangzhou Zhejiang 310000 - CN - 8C-8F-8B (hex) China Mobile Chongqing branch 8C8F8B (base 16) China Mobile Chongqing branch 6 building, No. 2, Xingguang three road @@ -154625,12 +155558,6 @@ A83CCB (base 16) ROSSMA 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 @@ -155468,5 +156395,275 @@ DC8C37 (base 16) Cisco Systems, Inc Nürnberg Bayern 90478 DE +1C-3A-60 (hex) Ruckus Wireless +1C3A60 (base 16) Ruckus Wireless + 350 West Java Drive + Sunnyvale CA 94089 + US + 7C-50-DA (hex) Private 7C50DA (base 16) Private + +BC-9F-E4 (hex) Aruba, a Hewlett Packard Enterprise Company +BC9FE4 (base 16) Aruba, a Hewlett Packard Enterprise Company + 3333 Scott Blvd + Santa Clara CA 95054 + US + +00-50-C6 (hex) LOOP TELECOMMUNICATION INTERNATIONAL, INC. +0050C6 (base 16) LOOP TELECOMMUNICATION INTERNATIONAL, INC. + 7F, No. 8, Hsin Ann Road + Hsinchu - + TW + +B0-A6-F5 (hex) Xaptum, Inc. +B0A6F5 (base 16) Xaptum, Inc. + 350 W Ontario ST FL 4 + Chicago IL 60654 + US + +E0-CB-1D (hex) Private +E0CB1D (base 16) Private + +48-77-46 (hex) Calix Inc. +487746 (base 16) Calix Inc. + 2777 Orchard Parkway + San Jose CA 95134 + US + +00-0B-E4 (hex) Hosiden Corporation +000BE4 (base 16) Hosiden Corporation + 4-33 + Yao-city Osaka 581-0071 + JP + +9C-7B-EF (hex) Hewlett Packard +9C7BEF (base 16) Hewlett Packard + 11445 Compaq Center Drive + Houston TX 77070 + US + +90-73-5A (hex) Motorola Mobility LLC, a Lenovo Company +90735A (base 16) Motorola Mobility LLC, a Lenovo Company + 222 West Merchandise Mart Plaza + Chicago IL 60654 + US + +A8-F9-4B (hex) Eltex Enterprise Ltd. +A8F94B (base 16) Eltex Enterprise Ltd. + Okruzhnaya st. 29v + Novosibirsk 630020 + RU + +F8-23-87 (hex) Shenzhen Horn Audio Co.,Ltd. +F82387 (base 16) Shenzhen Horn Audio Co.,Ltd. + NO.6 4th GuiHua road,PingShan, + Shenzhen Guangdong 518118 + CN + +D8-F1-5B (hex) Espressif Inc. +D8F15B (base 16) Espressif Inc. + Room 204, Building 2, 690 Bibo Rd, Pudong New Area + Shanghai Shanghai 201203 + CN + +78-D3-47 (hex) Ericsson AB +78D347 (base 16) Ericsson AB + Torshamnsgatan 36 + Stockholm SE-164 80 + SE + +9C-93-E4 (hex) Private +9C93E4 (base 16) Private + +7C-23-02 (hex) Samsung Electronics Co.,Ltd +7C2302 (base 16) Samsung Electronics Co.,Ltd + #94-1, Imsoo-Dong + Gumi Gyeongbuk 730-350 + KR + +28-D1-B7 (hex) Shenzhen YOUHUA Technology Co., Ltd +28D1B7 (base 16) Shenzhen YOUHUA Technology Co., Ltd + Room 407 Shenzhen University-town Business Park,Lishan Road,Taoyuan Street,Nanshan District + Shenzhen Guangdong 518055 + CN + +80-91-33 (hex) AzureWave Technology Inc. +809133 (base 16) AzureWave Technology Inc. + 8F., No. 94, Baozhong Rd. + New Taipei City Taiwan 231 + TW + +AC-83-E9 (hex) Beijing Zile Technology Co., Ltd +AC83E9 (base 16) Beijing Zile Technology Co., Ltd + Tecent WeWork, Huilongguan East Avenue, Changping District + Beijing Beijing 100096 + CN + +A4-00-E2 (hex) HUAWEI TECHNOLOGIES CO.,LTD +A400E2 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +D0-C6-5B (hex) HUAWEI TECHNOLOGIES CO.,LTD +D0C65B (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B4-C4-FC (hex) Xiaomi Communications Co Ltd +B4C4FC (base 16) Xiaomi Communications Co Ltd + The Rainbow City of China Resources + NO.68, Qinghe Middle Street Haidian District, Beijing 100085 + CN + +40-5B-D8 (hex) CHONGQING FUGUI ELECTRONICS CO.,LTD. +405BD8 (base 16) CHONGQING FUGUI ELECTRONICS CO.,LTD. + Building D21,No.1, East Zone 1st Road,Xiyong Town,Shapingba District + Chongqing Chongqing 401332 + CN + +E4-AB-89 (hex) MitraStar Technology Corp. +E4AB89 (base 16) MitraStar Technology Corp. + No. 6, Innovation Road II, + Hsinchu 300 + TW + +44-00-4D (hex) HUAWEI TECHNOLOGIES CO.,LTD +44004D (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B4-F5-8E (hex) HUAWEI TECHNOLOGIES CO.,LTD +B4F58E (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B4-9A-95 (hex) Shenzhen Boomtech Industrial Corporation +B49A95 (base 16) Shenzhen Boomtech Industrial Corporation + 2F, Block E, Bao'an Intelligent Valley, Yingtian Road No.4 Xixiang Sub-District Office, Bao'an District + Shenzhen Guangdong 518102 + CN + +00-0C-6C (hex) Eve Systems GmbH +000C6C (base 16) Eve Systems GmbH + Rotkreuzplatz 1 + Munich 80634 + DE + +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 + +A4-98-13 (hex) ARRIS Group, Inc. +A49813 (base 16) ARRIS Group, Inc. + 6450 Sequence Drive + San Diego CA 92121 + US + +F4-1D-6B (hex) HUAWEI TECHNOLOGIES CO.,LTD +F41D6B (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B4-A3-05 (hex) XIAMEN YAXON NETWORK CO., LTD. +B4A305 (base 16) XIAMEN YAXON NETWORK CO., LTD. + 46#,Guanri Road, Software Park II + Xiamen Fujian 361008 + CN + +58-C8-76 (hex) China Mobile (Hangzhou) Information Technology Co., Ltd. +58C876 (base 16) China Mobile (Hangzhou) Information Technology Co., Ltd. + No. 1600 Yuhangtang Road, Wuchang Street, Yuhang District + Hangzhou Zhejiang 310000 + CN + +38-EF-E3 (hex) INGENICO TERMINALS SAS +38EFE3 (base 16) INGENICO TERMINALS SAS + 28-32 BOULEVARD DE GRENELLE + PARIS 75015 + FR + +D0-37-45 (hex) TP-LINK TECHNOLOGIES CO.,LTD. +D03745 (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 + +50-D4-F7 (hex) TP-LINK TECHNOLOGIES CO.,LTD. +50D4F7 (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 + +8C-59-3C (hex) IEEE Registration Authority +8C593C (base 16) IEEE Registration Authority + 445 Hoes Lane + Piscataway NJ 08554 + US + +00-30-DA (hex) Comtrend Corporation +0030DA (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +64-68-0C (hex) Comtrend Corporation +64680C (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +D8-B6-B7 (hex) Comtrend Corporation +D8B6B7 (base 16) Comtrend Corporation + 3F-1, 10 Lane 609, Chongxin Road, Section 5, + New Taipei City, Taiwan 24159 + TW + +00-ED-B8 (hex) KYOCERA Corporation +00EDB8 (base 16) KYOCERA Corporation + 30 Hoji + Kitami, Hokkaido 099-1595 + JP + +00-F6-20 (hex) Google, Inc. +00F620 (base 16) Google, Inc. + 1600 Amphitheatre Parkway + Mountain View CA 94043 + US + +E4-15-F6 (hex) Texas Instruments +E415F6 (base 16) Texas Instruments + 12500 TI Blvd + Dallas TX 75243 + US + +B4-60-77 (hex) Sichuan Changhong Electric Ltd. +B46077 (base 16) Sichuan Changhong Electric Ltd. + No.35,East MianXin Road,MianYang,Sichaun,China. + MianYang SiChuan PRC 621000 + CN + +54-92-09 (hex) HUAWEI TECHNOLOGIES CO.,LTD +549209 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +E4-19-C1 (hex) HUAWEI TECHNOLOGIES CO.,LTD +E419C1 (base 16) HUAWEI TECHNOLOGIES CO.,LTD + No.2 Xin Cheng Road, Room R6,Songshan Lake Technology Park + Dongguan 523808 + CN + +B8-66-85 (hex) Sagemcom Broadband SAS +B86685 (base 16) Sagemcom Broadband SAS + 250, route de l'Empereur + Rueil Malmaison Cedex hauts de seine 92848 + FR diff --git a/hwdb/ma-medium.txt b/hwdb/ma-medium.txt index 3090a958f..4abc550c8 100644 --- a/hwdb/ma-medium.txt +++ b/hwdb/ma-medium.txt @@ -2426,9 +2426,6 @@ A0-19-B2 (hex) ARIMA Communications Corp. D0-D9-4F (hex) Private 700000-7FFFFF (base 16) Private -F4-0E-11 (hex) Private -F00000-FFFFFF (base 16) Private - B4-4B-D6 (hex) ShenZhen Comstar Technology Company 500000-5FFFFF (base 16) ShenZhen Comstar Technology Company Pengnian Science Park Building A 314 @@ -2843,12 +2840,6 @@ E4-1E-0A (hex) Avast Software s.r.o. Prague 4 14000 CZ -C8-2C-2B (hex) Fungible, Inc. -000000-0FFFFF (base 16) Fungible, Inc. - 3201 Scott Blvd., 2nd floor - Santa Clara CA 95054 - US - E4-1E-0A (hex) Shanghai LeXiang Technology Co., Ltd E00000-EFFFFF (base 16) Shanghai LeXiang Technology Co., Ltd Floor 6,Building 8,Yanjiaqiao Road,Pudong Area ,Shanghai ,China @@ -2867,6 +2858,12 @@ C8-2C-2B (hex) RF Engineering and Energy Resource Portage MI 49002 US +C8-2C-2B (hex) Fungible, Inc. +000000-0FFFFF (base 16) Fungible, Inc. + 3201 Scott Blvd., 2nd floor + Santa Clara CA 95054 + US + C8-2C-2B (hex) Repp Health 200000-2FFFFF (base 16) Repp Health 1919 14th Street , Suite 700 @@ -2894,6 +2891,123 @@ B0-FD-0B (hex) eSenseLab Ltd. 1C-CA-E3 (hex) Private F00000-FFFFFF (base 16) Private +B0-FD-0B (hex) TAE HYUNG Industrial Electronics Co., Ltd. +000000-0FFFFF (base 16) TAE HYUNG Industrial Electronics Co., Ltd. + 301-404, Bucheon Technopark, 345, Seokcheon-ro + Bucheon-si Gyeonggi-do 14501 + KR + +84-8B-CD (hex) TWTG R&D B.V. +600000-6FFFFF (base 16) TWTG R&D B.V. + Schaardijk 386 + Capelle aan den IJssel 2909LA + NL + +B0-FD-0B (hex) Everynet Oy +700000-7FFFFF (base 16) Everynet Oy + At Azets Insight Oy, Hatanpään valtatie 26 (5.krs) + Tampere 33100 + FI + +84-8B-CD (hex) SouXin Corporate +000000-0FFFFF (base 16) SouXin Corporate + 33 Jingyou Road + Nanjing Jiangsu 211100 + CN + +1C-82-59 (hex) 3xLOGIC Inc. +100000-1FFFFF (base 16) 3xLOGIC Inc. + 10385 Westmoor Dr, Suite 210 + Westminster CO 80021 + US + +1C-82-59 (hex) Applied Concepts, Inc. +D00000-DFFFFF (base 16) Applied Concepts, Inc. + 855 E Collins Blvd + Richardson TX 75081 + US + +60-95-CE (hex) Untangle, Inc. +400000-4FFFFF (base 16) Untangle, Inc. + 100 W. San Fernando St., Ste. 565 + San Jose CA 95113 + US + +BC-97-40 (hex) Amap Information Technology Co., Ltd +A00000-AFFFFF (base 16) Amap Information Technology Co., Ltd + A051, No.01, 3/F, No.55 Suzhou Street, Haidian District + Beijing Beijing 100080 + CN + +BC-97-40 (hex) LISTEC GmbH +C00000-CFFFFF (base 16) LISTEC GmbH + Am Sandberg 34 + Isen Bavaria 84424 + DE + +F4-0E-11 (hex) Private +F00000-FFFFFF (base 16) Private + +BC-97-40 (hex) Shanghai Laisi Information Technology Co.,Ltd +500000-5FFFFF (base 16) Shanghai Laisi Information Technology Co.,Ltd + 1001,21#,No.1158 Zhongxin RD,Songjiang district Shanghai + shanghai 201614 + CN + +BC-97-40 (hex) Shenzhen Colorwin Optical Technology Co.,Ltd +600000-6FFFFF (base 16) Shenzhen Colorwin Optical Technology Co.,Ltd + 201-2,2nd Floor,G3 Building, TCL International E City,1001 Zhongshanyuan Road,Nanshan District + Shenzhen Guangdong 518055 + CN + +D0-C8-57 (hex) Dante Security Inc. +C00000-CFFFFF (base 16) Dante Security Inc. + 303 MERRICK RD., Unit 208 + LYNBROOK NY 11563 + US + +BC-97-40 (hex) Lattec I/S +200000-2FFFFF (base 16) Lattec I/S + Blytækkervej 10 + Hillerød 3400 + DK + +D0-C8-57 (hex) Eco Mobile +700000-7FFFFF (base 16) Eco Mobile + Samoborska cesta 330 + Zagreb Zagreb 10090 + HR + +D0-C8-57 (hex) CHUNGHSIN INTERNATIONAL ELECTRONICS CO.,LTD. +B00000-BFFFFF (base 16) CHUNGHSIN INTERNATIONAL ELECTRONICS CO.,LTD. + 618-2# Gongren West Road,Jiaojiang, + Taizhou Zhejiang 317700 + CN + +D0-C8-57 (hex) E-T-A Elektrotechnische Apparate GmbH +E00000-EFFFFF (base 16) E-T-A Elektrotechnische Apparate GmbH + Industriestr. 2-8 + Altdorf 90518 + DE + +8C-59-3C (hex) GENIS +900000-9FFFFF (base 16) GENIS + 1111, 11F RACREUM, 26 Wiryeseoil-ro + Seongnam-si Gyeonggi-do 13647 + KR + +8C-59-3C (hex) Future Robot Technology Co., Limited +100000-1FFFFF (base 16) Future Robot Technology Co., Limited + Room 406 Block A South Wind Tower, Nanshan Cloud Valley Innovation Industry Park, No 4093 Lau Sin Avenue Taoyuan Street Nanshan District + Shenzhen Guangdong 518000 + CN + +8C-59-3C (hex) OBO Pro.2 Inc. +700000-7FFFFF (base 16) OBO Pro.2 Inc. + 2148 Bering Dr. + San Jose CA 95131 + US + 1C-87-76 (hex) Strone Technology C00000-CFFFFF (base 16) Strone Technology 13 Ellis Street @@ -4601,6 +4715,378 @@ C00000-CFFFFF (base 16) Shanghai Think-Force Electronic Technology Co. Ltd Norderstedt 22844 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) 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 + +FC-D2-B6 (hex) SHEN ZHEN XIN HAO YUAN PRECISION TECHNOLOGY CO.,L TD +400000-4FFFFF (base 16) SHEN ZHEN XIN HAO YUAN PRECISION TECHNOLOGY CO.,L TD + 1,2,3 Building,XinHaoYuan Industrial Area,HeYi Community,Shajing Street,BaoAn District.Shenzhen + shenzhen guangdongsheng 518000 + CN + +FC-D2-B6 (hex) Winglet Systems Inc. +900000-9FFFFF (base 16) Winglet Systems Inc. + 4-6, Shinyokohama 2-chome, Kohoku-ku + Yokohama Kanagawa 222-0033 + JP + +34-E1-D1 (hex) Doki Technologies Limited +500000-5FFFFF (base 16) Doki Technologies Limited + Unit 601,Tower One, Silvercord, 30 Canton Road, Tsim Sha Tsui + Kowloon 00000 + HK + +34-E1-D1 (hex) HI-TECH.ORG +D00000-DFFFFF (base 16) HI-TECH.ORG + Volgogradskiy prospekt, 43, k.3, room XXVI + Moscow 109316 + RU + +34-E1-D1 (hex) Tianjin Sublue Ocean Science & Technology Co., Ltd +000000-0FFFFF (base 16) Tianjin Sublue Ocean Science & Technology Co., Ltd + No.29 Factory No.156 Nanhai Road,TEDA + Tianjin 300050 + CN + +34-E1-D1 (hex) CREW by True Rowing, Inc. +C00000-CFFFFF (base 16) CREW by True Rowing, Inc. + 14 Arrow St, Floor 4 + Cambridge MA 02138 + US + +34-E1-D1 (hex) Annapurna labs +E00000-EFFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +C8-63-14 (hex) Autonics Co., Ltd. +100000-1FFFFF (base 16) Autonics Co., Ltd. + 4-14-26, Shimo-Muneoka + Shiki Saitama 3530003 + JP + +C8-63-14 (hex) GRINBI PARTNERS +600000-6FFFFF (base 16) GRINBI PARTNERS + 222, Dogok-ro, Gangnam-gu + Seoul 06272 + KR + +E4-1E-0A (hex) Safety Vision, LLC +B00000-BFFFFF (base 16) Safety Vision, LLC + 6100 West Sam Houston Parkway North + Houston TX 77041-5113 + US + +E4-1E-0A (hex) Zavod № 423 +000000-0FFFFF (base 16) Zavod № 423 + 2B, Zavodskoy proezd + Bogoroditsk Tula 301830 + RU + +C8-2C-2B (hex) Verifone Systems (China),lnc. +800000-8FFFFF (base 16) Verifone Systems (China),lnc. + 2nd Floor,No.39,Region C, Tongpan Road,Gulou District + fuzhou fujian 350004 + CN + +B0-FD-0B (hex) Taian Yuqi Communication Technology Co., Ltd +500000-5FFFFF (base 16) Taian Yuqi Communication Technology Co., Ltd + Building 4, Fengxiang Road, Tai'an Development Zone + Tai’an Shandong 271000 + CN + +B0-FD-0B (hex) Shenzhen FEIBIT Electronic Technology Co.,LTD +E00000-EFFFFF (base 16) Shenzhen FEIBIT Electronic Technology Co.,LTD + 5th floor,Bld. A1, Lilang Software Park + Shenzhen 518112 + CN + +B0-FD-0B (hex) IDspire Corporation Ltd. +100000-1FFFFF (base 16) IDspire Corporation Ltd. + 9F, No. 266, Sec. 1, Wenhua Rd., Banqiao Dist. + New Taipei City 22041 + TW + +84-8B-CD (hex) Shenzhen LTIME In-Vehicle Entertainment System Company Limited +100000-1FFFFF (base 16) Shenzhen LTIME In-Vehicle Entertainment System Company Limited + 4/F, Building 1, Nangang 1st Industrial Park No. 1029, Songbai Road, Xili, Nanshan District + SHENZHEN GUANGDONG 518055 + CN + 34-04-9E (hex) uikismart D00000-DFFFFF (base 16) uikismart Nanshan @@ -5009,9 +5495,6 @@ E00000-EFFFFF (base 16) GETMOBIT LLC BeiJing Beijing 100024 CN -A4-3B-FA (hex) Private -F00000-FFFFFF (base 16) Private - 8C-1C-DA (hex) Structura Technology & Innovation 300000-3FFFFF (base 16) Structura Technology & Innovation via Roveredo 20/B @@ -5312,359 +5795,80 @@ A4-ED-43 (hex) Linseis Messgeraete GmbH 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 +1C-82-59 (hex) C&A Marketing, INC. +300000-3FFFFF (base 16) C&A Marketing, INC. + 114 Tived Lane East + Edison NJ 08837 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 +1C-82-59 (hex) SHENZHEN AOA TECHNOLOGY CO.,LTD +800000-8FFFFF (base 16) SHENZHEN AOA TECHNOLOGY CO.,LTD + B624,C1 Building,Hengchao Industrial Park,Tangtou Road,Shiyan,Bao'an District + SHENZHEN Guangdong 518108 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 +60-95-CE (hex) Ponoor Experiments Inc. +100000-1FFFFF (base 16) Ponoor Experiments Inc. + Higashi-shinagawa 1-33-10, Terrada Art Complex 216 + Shinagawa-ku Tokyo 1400002 + JP -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 +60-95-CE (hex) AdvanWISE Corporation +500000-5FFFFF (base 16) AdvanWISE Corporation + No.11, Aly. 18, Ln. 85, Fuqun St., Xiangshan Dist. + Hsinchu 30067 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 +60-95-CE (hex) Beijing Sinomedisite Bio-tech Co.,Ltd +B00000-BFFFFF (base 16) Beijing Sinomedisite Bio-tech Co.,Ltd + No.9 Kangding Street,Economic-Technological Development Area + Beijing Beijing 100176 CN -74-1A-E0 (hex) Private -900000-9FFFFF (base 16) Private +60-95-CE (hex) Synamedia +C00000-CFFFFF (base 16) Synamedia + Luipaardstraat 12 + Kortrijk West-Vlaanderen 8500 + BE -4C-91-7A (hex) Openeye -600000-6FFFFF (base 16) Openeye - 23221 East Knox Avenue - Liberty Lake WA 99019 - US +60-95-CE (hex) Siema Applications +000000-0FFFFF (base 16) Siema Applications + 35 rue Alfred Brinon + Villeurbanne 69100 + FR -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 +BC-97-40 (hex) Alpha ESS Co., Ltd. +000000-0FFFFF (base 16) Alpha ESS Co., Ltd. + JiuHua Road 888, Nantong High-Tech Industrial Development Zone, + Nantong Jiangsu 226300 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 +BC-97-40 (hex) comtac AG +100000-1FFFFF (base 16) comtac AG + Allenwindenstrasse 1 + Flurlingen 8247 CH -1C-A0-D3 (hex) Private -700000-7FFFFF (base 16) Private +D0-C8-57 (hex) FORGAMERS INC. +200000-2FFFFF (base 16) FORGAMERS INC. + 6F., NO.51, DONGXING RD., XINYI DIST., TAIPEI CITY 110-70, TAIWAN + Taipei 110 + TW -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 +D0-C8-57 (hex) Nanjing Magewell Electronics Co.,Ltd +800000-8FFFFF (base 16) Nanjing Magewell Electronics Co.,Ltd + 14th Floor, Building 3, No. 89 Shengli Road, Jiangning Economic and Technological Development Zone + Nanjing Jiangsu 211100 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. +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 - -FC-D2-B6 (hex) SHEN ZHEN XIN HAO YUAN PRECISION TECHNOLOGY CO.,L TD -400000-4FFFFF (base 16) SHEN ZHEN XIN HAO YUAN PRECISION TECHNOLOGY CO.,L TD - 1,2,3 Building,XinHaoYuan Industrial Area,HeYi Community,Shajing Street,BaoAn District.Shenzhen - shenzhen guangdongsheng 518000 - CN - -FC-D2-B6 (hex) Winglet Systems Inc. -900000-9FFFFF (base 16) Winglet Systems Inc. - 4-6, Shinyokohama 2-chome, Kohoku-ku - Yokohama Kanagawa 222-0033 - JP - -34-E1-D1 (hex) Doki Technologies Limited -500000-5FFFFF (base 16) Doki Technologies Limited - Unit 601,Tower One, Silvercord, 30 Canton Road, Tsim Sha Tsui - Kowloon 00000 - HK - -34-E1-D1 (hex) HI-TECH.ORG -D00000-DFFFFF (base 16) HI-TECH.ORG - Volgogradskiy prospekt, 43, k.3, room XXVI - Moscow 109316 - RU - -34-E1-D1 (hex) Tianjin Sublue Ocean Science & Technology Co., Ltd -000000-0FFFFF (base 16) Tianjin Sublue Ocean Science & Technology Co., Ltd - No.29 Factory No.156 Nanhai Road,TEDA - Tianjin 300050 - CN - -34-E1-D1 (hex) CREW by True Rowing, Inc. -C00000-CFFFFF (base 16) CREW by True Rowing, Inc. - 14 Arrow St, Floor 4 - Cambridge MA 02138 - US - -34-E1-D1 (hex) Annapurna labs -E00000-EFFFFF (base 16) Annapurna labs - Matam Scientific Industries Center, Building 8.2 - Mail box 15123 Haifa 3508409 - IL - -C8-63-14 (hex) Autonics Co., Ltd. -100000-1FFFFF (base 16) Autonics Co., Ltd. - 4-14-26, Shimo-Muneoka - Shiki Saitama 3530003 - JP - -C8-63-14 (hex) GRINBI PARTNERS -600000-6FFFFF (base 16) GRINBI PARTNERS - 222, Dogok-ro, Gangnam-gu - Seoul 06272 - KR - -E4-1E-0A (hex) Safety Vision, LLC -B00000-BFFFFF (base 16) Safety Vision, LLC - 6100 West Sam Houston Parkway North - Houston TX 77041-5113 - US - -E4-1E-0A (hex) Zavod № 423 -000000-0FFFFF (base 16) Zavod № 423 - 2B, Zavodskoy proezd - Bogoroditsk Tula 301830 - RU - -C8-2C-2B (hex) Verifone Systems (China),lnc. -800000-8FFFFF (base 16) Verifone Systems (China),lnc. - 2nd Floor,No.39,Region C, Tongpan Road,Gulou District - fuzhou fujian 350004 - CN +A4-3B-FA (hex) Private +F00000-FFFFFF (base 16) Private 1C-87-76 (hex) Zhuhai MYZR Technology Co.,Ltd 500000-5FFFFF (base 16) Zhuhai MYZR Technology Co.,Ltd @@ -7724,9 +7928,6 @@ B00000-BFFFFF (base 16) COMATIS Leuven 3001 BE -80-E4-DA (hex) Private -F00000-FFFFFF (base 16) Private - 74-19-F8 (hex) Private F00000-FFFFFF (base 16) Private @@ -7736,9 +7937,6 @@ F00000-FFFFFF (base 16) Private Montreal Quebec H2Y 1P5 CA -BC-34-00 (hex) Private -F00000-FFFFFF (base 16) Private - 74-F8-DB (hex) Ballard Technology, Inc, A00000-AFFFFF (base 16) Ballard Technology, Inc, 11400 Airport Rd @@ -8009,9 +8207,6 @@ D4-7C-44 (hex) OMRON SENTECH CO., LTD. Ebina-City Kanagawa 243-0432 JP -88-5D-90 (hex) Private -F00000-FFFFFF (base 16) Private - B4-4B-D6 (hex) Taizhou convergence Information technology Co.,LTD 700000-7FFFFF (base 16) Taizhou convergence Information technology Co.,LTD Room 1006,general chamber of commerce,159 henghu road @@ -8675,6 +8870,135 @@ B0-FD-0B (hex) Vista Manufacturing Elkhart IN 46514 US +84-8B-CD (hex) CCX Technologies Inc. +200000-2FFFFF (base 16) CCX Technologies Inc. + 408 - 11 Rosemount Ave. + Ottawa Ontario K1Y 4R8 + CA + +84-8B-CD (hex) Emotiv Inc +E00000-EFFFFF (base 16) Emotiv Inc + 490 Post Street + San Francisco CA 94102 + US + +1C-82-59 (hex) winsun AG +400000-4FFFFF (base 16) winsun AG + Beeschi Mattenstrasse 2 + Steg Wallis 3940 + CH + +1C-82-59 (hex) Jump Trading +700000-7FFFFF (base 16) Jump Trading + 1 London Wall + London EC2Y 5EA + GB + +1C-82-59 (hex) Shandong Luneng Intelligence Technology CO., Ltd +000000-0FFFFF (base 16) Shandong Luneng Intelligence Technology CO., Ltd + Shandong Jinan Hightech zone Yinhe building block B, 2008 Xinluo Street + Jinan Shandong 250100 + CN + +1C-82-59 (hex) Diatrend Corporation +200000-2FFFFF (base 16) Diatrend Corporation + Grand Front Osaka Tower-B 28F, 3-1 ofuka-cho, kita-ku + Osaka Osaka 530-0011 + JP + +1C-82-59 (hex) KeyWest Networks, Inc +B00000-BFFFFF (base 16) KeyWest Networks, Inc + 2200 N Glassell St + Orange CA 92865 + US + +1C-82-59 (hex) Evondos Oy +C00000-CFFFFF (base 16) Evondos Oy + Salorankatu 5-7 + Salo 24240 + FI + +1C-82-59 (hex) Fagus-GreCon Greten GmbH & Co. KG +500000-5FFFFF (base 16) Fagus-GreCon Greten GmbH & Co. KG + Hannoversche Straße 58 + Alfeld 31061 + DE + +88-5D-90 (hex) Private +F00000-FFFFFF (base 16) Private + +1C-82-59 (hex) Microtronics Engineering GmbH +E00000-EFFFFF (base 16) Microtronics Engineering GmbH + Hauptstrasse 7 + Ruprechtshofen 3244 + AT + +60-95-CE (hex) GovComm +D00000-DFFFFF (base 16) GovComm + 3830 SW 30 Ave + Fort Lauderdale FL 33312 + US + +BC-34-00 (hex) Private +F00000-FFFFFF (base 16) Private + +60-95-CE (hex) Robot S.A. +300000-3FFFFF (base 16) Robot S.A. + Gremi de Cirurgians i Barbers 22 + Palma de Mallorca SPAIN / Balearic Islands 07009 + ES + +BC-97-40 (hex) Direct Communication Solutions +900000-9FFFFF (base 16) Direct Communication Solutions + 17150 Via del Campo, Ste 200 + San Diego CA 92127 + US + +BC-97-40 (hex) ForoTel +B00000-BFFFFF (base 16) ForoTel + 77, Shaumyana Str. + Rostov-na-Donu UFO 344079 + RU + +BC-97-40 (hex) Airfi Oy AB +700000-7FFFFF (base 16) Airfi Oy AB + Piilipuunkatu 11 + RAISIO 21200 + FI + +D0-C8-57 (hex) shenzhen cnsun +A00000-AFFFFF (base 16) shenzhen cnsun + 5 Floor, 2 Building,Tongfuyu Industrial City + shenzhen guangdong 518000 + CN + +D0-C8-57 (hex) YUAN High-Tech Development Co., Ltd. +000000-0FFFFF (base 16) YUAN High-Tech Development Co., Ltd. + 18F, No.88, Sec. 2, Chung Hsiao E.Rd., + Taipei, Taiwan 10050 + TW + +D0-C8-57 (hex) Beijing Inspiry Technology Co., Ltd. +500000-5FFFFF (base 16) Beijing Inspiry Technology Co., Ltd. + Building No. 5, East Zone, No. 10, Xibeiwang East Road, Haidian District + Beijing Beijing 100092 + CN + +80-E4-DA (hex) Private +F00000-FFFFFF (base 16) Private + +8C-59-3C (hex) Chongqing beimoting technology co.ltd +300000-3FFFFF (base 16) Chongqing beimoting technology co.ltd + 97-2 keyuan 2nd street, jiulongpo district, + Chongqing Chongqing 400039 + CN + +8C-59-3C (hex) IDRO-ELETTRICA S.P.A. +D00000-DFFFFF (base 16) IDRO-ELETTRICA S.P.A. + VIA BELLINI 2 + SAN CESARIO SUL PANARO ITALY/MODENA 41018 + IT + 1C-87-76 (hex) Hekatron Vertriebs GmbH B00000-BFFFFF (base 16) Hekatron Vertriebs GmbH Brühlmatten 9 @@ -10751,9 +11075,6 @@ D00000-DFFFFF (base 16) D-Link (Shanghai)Limited Corp. GYEONGGI-DO GOYANG-SI,ILSANDONG-GU 410315 KR -2C-D1-41 (hex) Private -F00000-FFFFFF (base 16) Private - 8C-1C-DA (hex) T+A elektroakustik GmbH & Co.KG B00000-BFFFFF (base 16) T+A elektroakustik GmbH & Co.KG Planckstr. 9-11 @@ -11510,18 +11831,18 @@ E4-1E-0A (hex) XPR Group Waterloo 1410 BE -C8-2C-2B (hex) Galgus -100000-1FFFFF (base 16) Galgus - Italica 1, 1st floor - Camas Seville 41900 - ES - C8-2C-2B (hex) Fränkische Rohrwerke Gebr. Kirchner GmbH & Co. KG E00000-EFFFFF (base 16) Fränkische Rohrwerke Gebr. Kirchner GmbH & Co. KG Hellinger Str. 1 Königsberg/Bayern 97486 DE +C8-2C-2B (hex) Galgus +100000-1FFFFF (base 16) Galgus + Italica 1, 1st floor + Camas Seville 41900 + ES + B0-FD-0B (hex) Haltian Products Oy C00000-CFFFFF (base 16) Haltian Products Oy Yrttipellontie 1D @@ -11534,6 +11855,105 @@ D00000-DFFFFF (base 16) Private 8C-14-7D (hex) Private 100000-1FFFFF (base 16) Private +B0-FD-0B (hex) Fasii Information Technology (Shanghai) Ltd. +400000-4FFFFF (base 16) Fasii Information Technology (Shanghai) Ltd. + Room 5011, Building 1, No.335 Guoding Road, Yangpu District + Shanghai Shanghai 200433 + CN + +84-8B-CD (hex) Sphera Telecom +A00000-AFFFFF (base 16) Sphera Telecom + Pos. Moscowskiy, 22-nd km of Kievskoye highway, household 4, building 2, body G, office 802G + Moscow 108811 + RU + +84-8B-CD (hex) NORALSY +900000-9FFFFF (base 16) NORALSY + 16 rue Lavoisier + Chennevieres sur Marne Ile de France 94430 + FR + +84-8B-CD (hex) CHONGQING HUAYI KANGDAO TECHNOLOGY CO.,LTD. +B00000-BFFFFF (base 16) CHONGQING HUAYI KANGDAO TECHNOLOGY CO.,LTD. + 14th Floor, Unicom Building, 192 Yuzhou Road, Yuzhong District + CHONGQING 410010 + CN + +1C-82-59 (hex) ESTec Corporation +A00000-AFFFFF (base 16) ESTec Corporation + 22, Yusangongdan 9-gil + Yangsan Gyeongsangnam-do 50592 + KR + +1C-82-59 (hex) Shanghai Xiaoyan Technology Co., Ltd. +900000-9FFFFF (base 16) Shanghai Xiaoyan Technology Co., Ltd. + Room 503, Building B, NO. 2305, Zuchongzhi Road + Shanghai Shanghai 201203 + CN + +1C-82-59 (hex) CGI IT UK LIMITED +600000-6FFFFF (base 16) CGI IT UK LIMITED + 20 Fenchurch Street, 14th Floor + London EC3M 3BY + GB + +60-95-CE (hex) Trophy SAS +800000-8FFFFF (base 16) Trophy SAS + 4 rue F. Pelloutier + Croissy-Beaubourg 77435 + FR + +60-95-CE (hex) Q-SENTECH Co.,Ltd. +200000-2FFFFF (base 16) Q-SENTECH Co.,Ltd. + #201,170 Seonyu-ro, Yeongdeungpo-gu + Seoul Korea 07255 + KR + +BC-97-40 (hex) Wind Mobility Technology (Beijing) Co., Ltd +400000-4FFFFF (base 16) Wind Mobility Technology (Beijing) Co., Ltd + 13603, Building 13, No. 2, Nanzhuzhang Hutong, Dongcheng District + Beijing Beijing 100010 + CN + +BC-97-40 (hex) Precision Galaxy Pvt. Ltd +300000-3FFFFF (base 16) Precision Galaxy Pvt. Ltd + 3rd Floor, No.22 Habibullah Road, T Nagar + CHENNAI,Tamil Nadu Tamilnadu 600017 + IN + +BC-97-40 (hex) Rollock Oy +D00000-DFFFFF (base 16) Rollock Oy + Viestitie 2 B + Kajaani 87700 + FI + +BC-97-40 (hex) B4ComTechnologies LLC +E00000-EFFFFF (base 16) B4ComTechnologies LLC + Office 2/6, 16a, Ivana Babushkina street + Moscow 117292 + RU + +D0-C8-57 (hex) Shenzhen xiaosha Intelligence Technology Co. Ltd +900000-9FFFFF (base 16) Shenzhen xiaosha Intelligence Technology Co. Ltd + Poly Building, 2702 Nanhai Avenue, Nanshan District + Shenzhen Guangdong 518054 + CN + +2C-D1-41 (hex) Private +F00000-FFFFFF (base 16) Private + +60-95-CE (hex) VNS Inc. +E00000-EFFFFF (base 16) VNS Inc. + 3F, No. 27, Lane 66, Ruiguang Rd. + Taipei Taiwan 11466 + TW + +8C-59-3C (hex) Nanonord A/S +800000-8FFFFF (base 16) Nanonord A/S + Skjernvej 4A + Aalborg 9220 + DK + 1C-87-74 (hex) Philips Personal Health Solutions 000000-0FFFFF (base 16) Philips Personal Health Solutions High Tech Campus, HTC37 floor 0 @@ -14449,3 +14869,123 @@ A00000-AFFFFF (base 16) TEMCO JAPAN CO., LTD. 80-7B-85 (hex) Private F00000-FFFFFF (base 16) Private + +B0-FD-0B (hex) Eagle Acoustics Manufacturing, LLC +900000-9FFFFF (base 16) Eagle Acoustics Manufacturing, LLC + 333 E IL ROUTE 83, Ste. 100 + MUNDELEIN IL 60060-4214 + US + +B0-FD-0B (hex) DNESO TEN Ltd. +600000-6FFFFF (base 16) DNESO TEN Ltd. + 1-2-28,gosho-doori,hyougo-ku + kobe hyougo 652-8510 + JP + +84-8B-CD (hex) Logic Supply +400000-4FFFFF (base 16) Logic Supply + 35 Thompson St + South Burlington VT 05403 + US + +84-8B-CD (hex) Smart Code (Shenzhen) Technology Co.,Ltd +700000-7FFFFF (base 16) Smart Code (Shenzhen) Technology Co.,Ltd + Keji North 3rd Rd + Shenzhen guangdong 518000 + CN + +84-8B-CD (hex) exodraft a/s +500000-5FFFFF (base 16) exodraft a/s + C. F. Teiegens Boulevard 41 + Odense SØ 5220 + DK + +84-8B-CD (hex) Annapurna labs +300000-3FFFFF (base 16) Annapurna labs + Matam Scientific Industries Center, Building 8.2 + Mail box 15123 Haifa 3508409 + IL + +84-8B-CD (hex) ENGISAT LDA +D00000-DFFFFF (base 16) ENGISAT LDA + RUA CENTRAL SAO LOURENÇO Nº 312 + VILAR DE ANDORINHO PORTO 4430-358 + PT + +60-95-CE (hex) Cadmo Soluciones SAC +700000-7FFFFF (base 16) Cadmo Soluciones SAC + Av. Angamos Este 2495 Dpt. 301, San Borja + Lima Lima 15036 + PE + +60-95-CE (hex) Xiamen Sigmastar Technology Ltd. +600000-6FFFFF (base 16) Xiamen Sigmastar Technology Ltd. + 15th Floor ,Unit A,Chuangxin Building, Software Park, Xiamen Torch Hi-Tech Industrial Development Zone, Xiamen,China + Xiamen Fujian 361005 + CN + +60-95-CE (hex) (UN)MANNED +A00000-AFFFFF (base 16) (UN)MANNED + Baron Ruzettelaan 3 + Brugge 8310 + BE + +60-95-CE (hex) Jlztlink Industry(ShenZhen)Co.,Ltd. +900000-9FFFFF (base 16) Jlztlink Industry(ShenZhen)Co.,Ltd. + D-502#,Tongan logistics center,Sanwei hangkong 30#,Xiang street,Baoan, + Shenzhen Guangdong 518000 + CN + +BC-97-40 (hex) Gaodi Rus +800000-8FFFFF (base 16) Gaodi Rus + Korneeva, 14 + Elektrostal Moscow region 144009 + RU + +D0-C8-57 (hex) IFLYTEK CO.,LTD. +D00000-DFFFFF (base 16) IFLYTEK CO.,LTD. + National Intelligent Speech High-tech Industrialization Base, No. 666, Wangjiang Road West, + Heifei An hui 230088 + CN + +D0-C8-57 (hex) Mobicon +300000-3FFFFF (base 16) Mobicon + #406, 97, Jungbu-daero 448beon-gil, Yeongtong-gu + Suwon-si Gyeonggi-do 16521 + KR + +D0-C8-57 (hex) DALI A/S +100000-1FFFFF (base 16) DALI A/S + Dali Alle 1 + Norager 9610 + DK + +D0-C8-57 (hex) Imin Technology Pte Ltd +400000-4FFFFF (base 16) Imin Technology Pte Ltd + 77 Sciene park Drive #03-10 Cintech III + Singapore Singapore 118256 + SG + +D0-C8-57 (hex) Innovative Industrial(HK)Co., Limited +600000-6FFFFF (base 16) Innovative Industrial(HK)Co., Limited + OFFICE 3A,12/F,KAISER CENTRE,NO.18CENTRE STREET + SAI YING PUN 999077 + HK + +8C-59-3C (hex) Spectranetix +500000-5FFFFF (base 16) Spectranetix + 845 Stewart Drive, Suite B + Sunnyvale CA 94085 + US + +8C-59-3C (hex) Qbic Technology Co., Ltd +600000-6FFFFF (base 16) Qbic Technology Co., Ltd + 26F.-12, No.99, Sec. 1, Xintai 5th Rd., Xizhi Dist., + New Taipei 22175 + TW + +8C-59-3C (hex) Beida Jade Bird Universal Fire Alarm Device CO.,LTD. +200000-2FFFFF (base 16) Beida Jade Bird Universal Fire Alarm Device CO.,LTD. + Jade Bird Building C, 207 Chengfu RD, Haidian District + Beijing Beijing 100871 + CN diff --git a/hwdb/ma-small.txt b/hwdb/ma-small.txt index abafe2166..e9c20f28b 100644 --- a/hwdb/ma-small.txt +++ b/hwdb/ma-small.txt @@ -2186,12 +2186,6 @@ FAF000-FAFFFF (base 16) Radig Hard & Software Northfield IL 60093 US -70-B3-D5 (hex) Alpha ESS Co., Ltd. -7A2000-7A2FFF (base 16) Alpha ESS Co., Ltd. - JiuHua Road 888, Nantong High-Tech Industrial Development Zone, - Nantong Jiangsu 226300 - CN - 70-B3-D5 (hex) Quan International Co., Ltd. 724000-724FFF (base 16) Quan International Co., Ltd. 4F, No. 196, Hsinghu 3rd Rd., Neihu District @@ -3506,6 +3500,108 @@ EAE000-EAEFFF (base 16) Orlaco Products B.V. Montrevault sur Evre Maine et Loire 49110 FR +70-B3-D5 (hex) Acrodea, Inc. +4A8000-4A8FFF (base 16) Acrodea, Inc. + 3F, Daisan Yamada Bldg., 22 Aizumi-cho + Shinjuku-ku Tokyo 1600005 + JP + +70-B3-D5 (hex) SamabaNova Systems +F29000-F29FFF (base 16) SamabaNova Systems + 2100 Geng Rd #103 + Palo Alto CA 94303 + US + +70-B3-D5 (hex) TimeMachines Inc. +5A6000-5A6FFF (base 16) TimeMachines Inc. + 300 S 68th Street Place, Suite 100 + Lincoln NE 68510 + US + +70-B3-D5 (hex) INTECH +E9D000-E9DFFF (base 16) INTECH + Vavilova st,13/7 + Moscow 117312 + RU + +70-B3-D5 (hex) KeyW Corporation +7CB000-7CBFFF (base 16) KeyW Corporation + 7763 Old Telegraph Road + Severn MD 21144 + US + +70-B3-D5 (hex) Viper Innovations Ltd +A45000-A45FFF (base 16) Viper Innovations Ltd + 45 Martingale Way + Bristol BS20 7AW + GB + +70-B3-D5 (hex) microtec Sicherheitstechnik GmbH +DFE000-DFEFFF (base 16) microtec Sicherheitstechnik GmbH + Auf der Langwies 20 + Hünstetten 65510 + DE + +70-B3-D5 (hex) Alpha ESS Co., Ltd. +7A2000-7A2FFF (base 16) Alpha ESS Co., Ltd. + JiuHua Road 888, Nantong High-Tech Industrial Development Zone, + Nantong Jiangsu 226300 + CN + +70-B3-D5 (hex) VITEC +0CA000-0CAFFF (base 16) VITEC + 99, rue Pierre Semard + CHATILLON 92320 + FR + +70-B3-D5 (hex) Onethinx BV +068000-068FFF (base 16) Onethinx BV + Punterweg 2 + Zwolle OV 8042 PB + NL + +70-B3-D5 (hex) Worldsensing +2C7000-2C7FFF (base 16) Worldsensing + Carrer Viriat 47, Edificio Numancia 1 7th floor + Barcelona - Please Choose - 08014 + ES + +70-B3-D5 (hex) Fath Mechatronics +DC3000-DC3FFF (base 16) Fath Mechatronics + Hügelmühle 31 + Spalt bavaria 91174 + DE + +70-B3-D5 (hex) Oxford Monitoring Solutions Ltd +102000-102FFF (base 16) Oxford Monitoring Solutions Ltd + Oakfield Estate + Eynsham Oxford OX298JG + GB + +70-B3-D5 (hex) Sensative AB +2C0000-2C0FFF (base 16) Sensative AB + Mobilvägen 10 + Lund 22362 + SE + +70-B3-D5 (hex) Astronomical Research Cameras, Inc. +24C000-24CFFF (base 16) Astronomical Research Cameras, Inc. + 2247 San Diego Ave #135 + San Diego CA 92110 + US + +70-B3-D5 (hex) Sicon srl +D49000-D49FFF (base 16) Sicon srl + Via Sila 1/3 + Isola Vicentina Vicenza 36033 + IT + +70-B3-D5 (hex) ATGS +726000-726FFF (base 16) ATGS + 11 POLBINA ST. + MOSCOW 109388 + RU + 70-B3-D5 (hex) Flintab AB D60000-D60FFF (base 16) Flintab AB Kabelvägen 4 @@ -5861,6 +5957,456 @@ C1C000-C1CFFF (base 16) D.E.M. SPA Longarone (BL) 32013 IT +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) 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) OBSERVER FOUNDATION +633000-633FFF (base 16) OBSERVER FOUNDATION + Narva mnt 5 + Tallinn city Harju county 10117 + EE + +70-B3-D5 (hex) MECT SRL +7C4000-7C4FFF (base 16) MECT SRL + VIA E. FERMI 57/59 + ALPIGNANO 10091 + IT + +70-B3-D5 (hex) Benetel +E15000-E15FFF (base 16) Benetel + Guinness Enterprise Centre, Taylors Lane, + Dublin D08 XV25 + IE + +70-B3-D5 (hex) XANTIA SA +33F000-33FFFF (base 16) XANTIA SA + Chemin du Longchamps 99 + Bienne 2504 + CH + +70-B3-D5 (hex) Divelbiss Corporation +F43000-F43FFF (base 16) Divelbiss Corporation + 9778 Mount Gilead Road + Fredericktown OH 43019 + US + +70-B3-D5 (hex) INTERNET PROTOCOLO LOGICA SL +275000-275FFF (base 16) INTERNET PROTOCOLO LOGICA SL + Sector Foresta 43, local 26 + Tres Cantos Madrid 28760 + ES + +70-B3-D5 (hex) Integrotech sp. z o.o. +6BA000-6BAFFF (base 16) Integrotech sp. z o.o. + plac Zwyciestwa 2 bud. D + Lodz lodzkie 90-312 + PL + +70-B3-D5 (hex) Invent Vision - iVision Sistemas de Imagem e Visão S.A. +E29000-E29FFF (base 16) Invent Vision - iVision Sistemas de Imagem e Visão S.A. + R. Prof. José Vieira de Mendonça, 770, 2° andar - BHTEC, Parque Tecnológico de Belo Horizonte + Belo Horizonte Minas Gerais 31310-260 + BR + +70-B3-D5 (hex) Altaneos +69A000-69AFFF (base 16) Altaneos + Chaussée Verte, 93B + Saint-Georges 4470 + BE + +70-B3-D5 (hex) Sicon srl +BEE000-BEEFFF (base 16) Sicon srl + Via Sila 1/3 + Isola Vicentina Vicenza 36033 + IT + +70-B3-D5 (hex) Flexsolution APS +C54000-C54FFF (base 16) Flexsolution APS + Østervangsvej 39 + Esbjerg N Jylland 6715 + DK + +70-B3-D5 (hex) Private +E2D000-E2DFFF (base 16) Private + +70-B3-D5 (hex) EXARA Group +00C000-00CFFF (base 16) EXARA Group + Andropova pr. 18 1 + Moscow 115432 + RU + +70-B3-D5 (hex) Orlaco Products B.V. +620000-620FFF (base 16) Orlaco Products B.V. + Albert Plesmanstraat 42 + Barneveld 3772MN + NL + +70-B3-D5 (hex) LANSITEC TECHNOLOGY CO., LTD +A4D000-A4DFFF (base 16) LANSITEC TECHNOLOGY CO., LTD + No. 8 Huashen Avenue + Nanjing Jiangsu 210012 + CN + +70-B3-D5 (hex) Tonbo Imaging Pte Ltd +506000-506FFF (base 16) Tonbo Imaging Pte Ltd + 77 Science Park Drive,CINTECH III, Singapore Science Park I + Singapore 118256 + SG + +70-B3-D5 (hex) OHASHI ENGINEERING CO.,LTD. +556000-556FFF (base 16) OHASHI ENGINEERING CO.,LTD. + 1-471-8 TOYONODAI + KAZO-CITY SAITAMA 349-1148 + JP + +70-B3-D5 (hex) iMAR Navigation GmbH +A42000-A42FFF (base 16) iMAR Navigation GmbH + Im Reihersbruch 3 + St. Ingbert Saarland 66386 + DE + +70-B3-D5 (hex) QUERCUS TECHNOLOGIES, S.L. +40B000-40BFFF (base 16) QUERCUS TECHNOLOGIES, S.L. + Av. Onze de Setembre 19 + Reus Tarragona 43203 + ES + +70-B3-D5 (hex) AVA Monitoring AB +31D000-31DFFF (base 16) AVA Monitoring AB + Vädursgatan 6 + Göteborg Västra götaland 412 50 + SE + 70-B3-D5 (hex) PTYPE Co., LTD. 6B0000-6B0FFF (base 16) PTYPE Co., LTD. B121, B-dong, Keumkang Penterium IT Tower, 810, Gwanyand 2-dong, Dongan-gu @@ -6215,12 +6761,6 @@ F37000-F37FFF (base 16) Mitsubishi Electric Micro-Computer Application Soft Marlborough MA 01752 US -70-B3-D5 (hex) RCH Italia SpA -42D000-42DFFF (base 16) RCH Italia SpA - Via Cendon 39 - SILEA Treviso 31057 - IT - 70-B3-D5 (hex) InnoSenT 332000-332FFF (base 16) InnoSenT Am Roedertor 30 @@ -6647,9 +7187,6 @@ FEA000-FEAFFF (base 16) Heng Dian Technology Co., Ltd Moscow 109388 RU -70-B3-D5 (hex) Private -401000-401FFF (base 16) Private - 70-B3-D5 (hex) LG Electronics 9E3000-9E3FFF (base 16) LG Electronics 10, Magokjungang 10-ro, Gangseo-gu @@ -6881,422 +7418,83 @@ A5F000-A5FFFF (base 16) Daatrics LTD 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 +70-B3-D5 (hex) AMS Controls, Inc. +444000-444FFF (base 16) AMS Controls, Inc. + 12180 Prichard Farm Road + Maryland Heights MO 63043 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) 3S - Sensors, Signal Processing, Systems GmbH +982000-982FFF (base 16) 3S - Sensors, Signal Processing, Systems GmbH + Saar-Lor-Lux-Straße 11 + Saarbrücken 66115 + DE -70-B3-D5 (hex) Private -B71000-B71FFF (base 16) Private +70-B3-D5 (hex) AMEDTEC Medizintechnik Aue GmbH +50A000-50AFFF (base 16) AMEDTEC Medizintechnik Aue GmbH + Schneeberger Str. 5 + Aue 08280 + DE -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) RCH ITALIA SPA +42D000-42DFFF (base 16) RCH ITALIA SPA + Via Cendon 39 + SILEA Treviso 31057 + IT -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) BESO sp. z o.o. +C00000-C00FFF (base 16) BESO sp. z o.o. + Mlynska 1a + Brzeg Dolny 56-120 + PL -70-B3-D5 (hex) Telefire -2E8000-2E8FFF (base 16) Telefire - 43 hasivim - Petah Tikva Israel 49000 - IL +70-B3-D5 (hex) Carlo Gavazzi Industri +EFF000-EFFFFF (base 16) Carlo Gavazzi Industri + Over Hadstenvej 40 + Hadsten Denmark 8370 + DK -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) RCH ITALIA SPA +99B000-99BFFF (base 16) RCH ITALIA SPA + VIA CENDON 39 + SILEA TREVISO 31057 + IT -70-B3-D5 (hex) eze System, Inc. -2F5000-2F5FFF (base 16) eze System, Inc. - 785 Orchard Dr #100 - Folsom CA 95630 - US +70-B3-D5 (hex) Arevita +5E1000-5E1FFF (base 16) Arevita + Baltu ave 145 + Kaunas Select 47125 + LT -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) AutomationX GmbH +CD7000-CD7FFF (base 16) AutomationX GmbH + Lauzilgasse 13 + Graz Styria 8020 + AT 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 +401000-401FFF (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) OBSERVER FOUNDATION -633000-633FFF (base 16) OBSERVER FOUNDATION - Narva mnt 5 - Tallinn city Harju county 10117 - EE - -70-B3-D5 (hex) MECT SRL -7C4000-7C4FFF (base 16) MECT SRL - VIA E. FERMI 57/59 - ALPIGNANO 10091 - IT - -70-B3-D5 (hex) Benetel -E15000-E15FFF (base 16) Benetel - Guinness Enterprise Centre, Taylors Lane, - Dublin D08 XV25 - IE - -70-B3-D5 (hex) XANTIA SA -33F000-33FFFF (base 16) XANTIA SA - Chemin du Longchamps 99 - Bienne 2504 - CH - -70-B3-D5 (hex) Divelbiss Corporation -F43000-F43FFF (base 16) Divelbiss Corporation - 9778 Mount Gilead Road - Fredericktown OH 43019 - US +70-B3-D5 (hex) Hermann Automation GmbH +B2F000-B2FFFF (base 16) Hermann Automation GmbH + Erlenwiese 15 + Mengerskirchen Hessen D-35794 + DE 70-B3-D5 (hex) INTERNET PROTOCOLO LOGICA SL -275000-275FFF (base 16) INTERNET PROTOCOLO LOGICA SL +B58000-B58FFF (base 16) INTERNET PROTOCOLO LOGICA SL Sector Foresta 43, local 26 Tres Cantos Madrid 28760 ES -70-B3-D5 (hex) Integrotech sp. z o.o. -6BA000-6BAFFF (base 16) Integrotech sp. z o.o. - plac Zwyciestwa 2 bud. D - Lodz lodzkie 90-312 - PL - -70-B3-D5 (hex) Invent Vision - iVision Sistemas de Imagem e Visão S.A. -E29000-E29FFF (base 16) Invent Vision - iVision Sistemas de Imagem e Visão S.A. - R. Prof. José Vieira de Mendonça, 770, 2° andar - BHTEC, Parque Tecnológico de Belo Horizonte - Belo Horizonte Minas Gerais 31310-260 - BR - -70-B3-D5 (hex) Altaneos -69A000-69AFFF (base 16) Altaneos - Chaussée Verte, 93B - Saint-Georges 4470 - BE - -70-B3-D5 (hex) Sicon srl -BEE000-BEEFFF (base 16) Sicon srl - Via Sila 1/3 - Isola Vicentina Vicenza 36033 - IT - -70-B3-D5 (hex) Private -E2D000-E2DFFF (base 16) Private - -70-B3-D5 (hex) Flexsolution APS -C54000-C54FFF (base 16) Flexsolution APS - Østervangsvej 39 - Esbjerg N Jylland 6715 - DK - -70-B3-D5 (hex) EXARA Group -00C000-00CFFF (base 16) EXARA Group - Andropova pr. 18 1 - Moscow 115432 - RU - -70-B3-D5 (hex) Orlaco Products B.V. -620000-620FFF (base 16) Orlaco Products B.V. - Albert Plesmanstraat 42 - Barneveld 3772MN - NL +70-B3-D5 (hex) Legrand Electric Ltd +093000-093FFF (base 16) Legrand Electric Ltd + Unit 10 Cowley Road + Blyth Northumberland NE24 5TF + GB 70-B3-D5 (hex) Schildknecht AG 494000-494FFF (base 16) Schildknecht AG @@ -10058,12 +10256,6 @@ FAE000-FAEFFF (base 16) Silixa Ltd Virginia Beach VA 23452 US -70-B3-D5 (hex) RCH Italia SpA -7FE000-7FEFFF (base 16) RCH Italia SpA - Via Cendon 39 - SILEA Treviso 31057 - IT - 70-B3-D5 (hex) Korea Airports Corporation 2CD000-2CDFFF (base 16) Korea Airports Corporation 78 Haneul-gil Gangseo-gu @@ -10295,9 +10487,6 @@ C66000-C66FFF (base 16) Blue Access Inc CATTOLICA RIMINI 47841 IT -70-B3-D5 (hex) Private -A9F000-A9FFFF (base 16) Private - 70-B3-D5 (hex) Burk Technology 985000-985FFF (base 16) Burk Technology 7 Beaver Brook road @@ -10911,10 +11100,10 @@ BA8000-BA8FFF (base 16) Controlled Power Company US 70-B3-D5 (hex) Private -30E000-30EFFF (base 16) Private +4F8000-4F8FFF (base 16) Private 70-B3-D5 (hex) Private -4F8000-4F8FFF (base 16) Private +30E000-30EFFF (base 16) Private 70-B3-D5 (hex) Vega-Absolute 0AD000-0ADFFF (base 16) Vega-Absolute @@ -10934,6 +11123,144 @@ DD9000-DD9FFF (base 16) MaNima Technologies BV SWANSEA WEST GLAMORGAN SA6 8RH GB +70-B3-D5 (hex) PAL Inc. +05B000-05BFFF (base 16) PAL Inc. + 2217-2 Hayashicho + Takamatsu Kagawa 7610301 + JP + +70-B3-D5 (hex) Clever Devices +A2B000-A2BFFF (base 16) Clever Devices + 300 Crossways Park Dr + Woodbury NY 11797 + US + +70-B3-D5 (hex) NanoSense +8ED000-8EDFFF (base 16) NanoSense + 123 rue de Bellevue, RDC + Boulogne Billancourt 92100 + FR + +70-B3-D5 (hex) Blake UK +9DA000-9DAFFF (base 16) Blake UK + 177-187, Rutland Road + Sheffield --select-- S3 9PT + GB + +70-B3-D5 (hex) Angora Networks +822000-822FFF (base 16) Angora Networks + Alacaatli Mah. Park Cad 47/31, Cayyolu + Ankara 06810 + TR + +70-B3-D5 (hex) Thruvision Limited +4FA000-4FAFFF (base 16) Thruvision Limited + 121 Olympic Avenue, Milton Park + Abingdon Oxfordshire OX14 4SA + GB + +70-B3-D5 (hex) ABB +080000-080FFF (base 16) ABB + Fulachstrasse 150 + Schaffhausen 8200 + CH + +70-B3-D5 (hex) Transit Solutions, LLC. +019000-019FFF (base 16) Transit Solutions, LLC. + 114 West Grandview Avenue + Zelienople 16063 + US + +70-B3-D5 (hex) Virsae Group Ltd +151000-151FFF (base 16) Virsae Group Ltd + B:HIVE, 74 Taharoto Road, Smales Farm + Takapuna Auckland 0622 + NZ + +70-B3-D5 (hex) FRESENIUS MEDICAL CARE +4CC000-4CCFFF (base 16) FRESENIUS MEDICAL CARE + 496 So. Abbott Ave + Milpitas CA 95035 + US + +70-B3-D5 (hex) Leonardo Sistemi Integrati S.r.l. +111000-111FFF (base 16) Leonardo Sistemi Integrati S.r.l. + Via Greto di Cornigliano, 6R + Genova 16152 + IT + +70-B3-D5 (hex) RCH ITALIA SPA +7FE000-7FEFFF (base 16) RCH ITALIA SPA + Via Cendon 39 + SILEA Treviso 31057 + IT + +70-B3-D5 (hex) AEye, Inc. +B52000-B52FFF (base 16) AEye, Inc. + 5700 Stoneridge Drive, Suite 102 + Pleasanton CA 94588 + US + +70-B3-D5 (hex) GJD Manufacturing +E25000-E25FFF (base 16) GJD Manufacturing + Unit 2, Birch Business Park, Whittle Lane + Heywood OL10 2SX + GB + +70-B3-D5 (hex) Melecs EWS GmbH +E8A000-E8AFFF (base 16) Melecs EWS GmbH + GZO-Technologiestrasse 1 + Siegendorf 7011 + AT + +70-B3-D5 (hex) Umano Medical Inc. +E1E000-E1EFFF (base 16) Umano Medical Inc. + 230, Boul. Nilus-Leclerc + L'Islet Québec G0R 2C0 + CA + +70-B3-D5 (hex) DORLET SAU +8DF000-8DFFFF (base 16) DORLET SAU + Albert Eistein 34 + Alava SPAIN 01510 + ES + +70-B3-D5 (hex) Private +A9F000-A9FFFF (base 16) Private + +70-B3-D5 (hex) KRONOTECH SRL +D56000-D56FFF (base 16) KRONOTECH SRL + VIALE UNGHERIA 125 + UDINE ITALY/UDINE 33100 + IT + +70-B3-D5 (hex) Ascon Tecnologic S.r.l. +57E000-57EFFF (base 16) Ascon Tecnologic S.r.l. + via Indipendenza, 56 + Vigevano PV 27029 + IT + +70-B3-D5 (hex) Private +2D7000-2D7FFF (base 16) Private + +70-B3-D5 (hex) GeoSpectrum Technologies Inc +3CB000-3CBFFF (base 16) GeoSpectrum Technologies Inc + 10 Akerley Blvd, #19 + Dartmouth Nova Scotia B3B1J4 + CA + +70-B3-D5 (hex) Heim- & Bürokommunikation Ilmert e.K. +DF4000-DF4FFF (base 16) Heim- & Bürokommunikation Ilmert e.K. + Jakobsbrunnenstr. 14 + Frankfurt am Main 60386 + DE + +70-B3-D5 (hex) Echogear +571000-571FFF (base 16) Echogear + 12884 Frontrunner Blvd Ste 201 + Draper UT 84020 + US + 70-B3-D5 (hex) Innitive B.V. 66B000-66BFFF (base 16) Innitive B.V. Brouwerijstraat 20 @@ -13982,12 +14309,6 @@ A54000-A54FFF (base 16) provedo Waterloo 1410 BE -70-B3-D5 (hex) RCH Italia SpA -857000-857FFF (base 16) RCH Italia SpA - Via Cendon 39 - SILEA Treviso 31057 - IT - 70-B3-D5 (hex) IONETECH 876000-876FFF (base 16) IONETECH #50,sicheongseo4gil @@ -14687,6 +15008,138 @@ D3A000-D3AFFF (base 16) PROMOMED RUS LLC SEONGNAM-SI JUNGWON-GU 13376 KR +70-B3-D5 (hex) Clean-Lasersysteme GmbH +C5F000-C5FFFF (base 16) Clean-Lasersysteme GmbH + Dornkaulstr. 6-8 + Herzogenrath NRW 52134 + DE + +70-B3-D5 (hex) ELINKGATE JSC +37E000-37EFFF (base 16) ELINKGATE JSC + J4A Buu Long Dist 10 + Ho Chi Minh Vietnam 70350 + VN + +70-B3-D5 (hex) Automata GmbH & Co. KG +1F9000-1F9FFF (base 16) Automata GmbH & Co. KG + Gewerbering 5 + Ried Bavaria 86510 + DE + +70-B3-D5 (hex) EYEDEA +FB9000-FB9FFF (base 16) EYEDEA + Rm.367, 3Fl., Enterprise Support Hub, 815, Daewangpangyo-ro, Sujeong-gu + Seongnam-si Gyeonggi-do 13449 + KR + +70-B3-D5 (hex) Automata Spa +E14000-E14FFF (base 16) Automata Spa + Via Carducci 705 + Caronno Pertusella Varese 21042 + IT + +70-B3-D5 (hex) Grossenbacher Systeme AG +BB5000-BB5FFF (base 16) Grossenbacher Systeme AG + Spinnereistrasse 10 + St. Gallen 9008 + CH + +70-B3-D5 (hex) APE GmbH +8C4000-8C4FFF (base 16) APE GmbH + Plauener Str. 163-165, Haus N + Berlin 13053 + DE + +70-B3-D5 (hex) RCH ITALIA SPA +857000-857FFF (base 16) RCH ITALIA SPA + Via Cendon 39 + SILEA Treviso 31057 + IT + +70-B3-D5 (hex) Eastone Century Technology Co.,Ltd. +210000-210FFF (base 16) Eastone Century Technology Co.,Ltd. + A12 Floor, Information Port, 16 Keyun Road, Tianhe DistrictGuangzhou + Guangzhou GuangDong 510000 + CN + +70-B3-D5 (hex) RITEC +AC0000-AC0FFF (base 16) RITEC + 25 East Easy St + Simi Valley CA 93065 + US + +70-B3-D5 (hex) INFRASAFE/ ADVANTOR SYSTEMS +5BA000-5BAFFF (base 16) INFRASAFE/ ADVANTOR SYSTEMS + 12612 CHALLENGER PARKWAY + ORLANDO FL 32826 + US + +70-B3-D5 (hex) Yite technology +BC9000-BC9FFF (base 16) Yite technology + No. 56, Xiaobei Rd., North Dist + Tainan 70448 + TW + +70-B3-D5 (hex) NTO IRE-POLUS +6FE000-6FEFFF (base 16) NTO IRE-POLUS + Akademika Vvedenskogo 1/2 + Fryazino 141190 + RU + +70-B3-D5 (hex) Idneo Technologies S.A.U. +CF8000-CF8FFF (base 16) Idneo Technologies S.A.U. + Gran Via Carlos III , 98 Planta 5 + Barcelona Barcelona 08028 + ES + +70-B3-D5 (hex) Syscom Instruments SA +BE7000-BE7FFF (base 16) Syscom Instruments SA + industrie 21 + Sainte-Croix 1450 + CH + +70-B3-D5 (hex) DAO QIN TECHNOLOGY CO.LTD. +3BD000-3BDFFF (base 16) DAO QIN TECHNOLOGY CO.LTD. + No. 359, Zhongxiao Rd + Chishang Township Taitung County 958 + TW + +70-B3-D5 (hex) MB connect line GmbH Fernwartungssysteme +CC6000-CC6FFF (base 16) MB connect line GmbH Fernwartungssysteme + Winnettener Straße 6 + Dinkelsbuehl Bavaria 91550 + DE + +70-B3-D5 (hex) Technological Application and Production One Member Liability Company (Tecapro company) +456000-456FFF (base 16) Technological Application and Production One Member Liability Company (Tecapro company) + 18A Cong Hoa street + Ho Chi Minh city Ho Chi Minh city 700000 + VN + +70-B3-D5 (hex) Yamamoto Works Ltd. +DFB000-DFBFFF (base 16) Yamamoto Works Ltd. + 3-1-3 Kashiwaza + Ageo Saitama 3620075 + JP + +70-B3-D5 (hex) BAYKON Endüstriyel Kontrol Sistemleri San. ve Tic. A.Ş. +258000-258FFF (base 16) BAYKON Endüstriyel Kontrol Sistemleri San. ve Tic. A.Ş. + Kimya Sanayicileri Org. San. Bolgesi Organik Cad. No:31 + Istanbul Tuzla 34956 + TR + +70-B3-D5 (hex) GlooVir Inc. +47A000-47AFFF (base 16) GlooVir Inc. + #413, 96 Gajeongbuk-Ro + Daejeon Yuseong 34111 + KR + +70-B3-D5 (hex) WARECUBE,INC +463000-463FFF (base 16) WARECUBE,INC + #A-811, 142-10, Saneop-ro, 156beon-gil, Gwonseon-gu + Suwon-si 16648 + KR + 70-B3-D5 (hex) EMAC, Inc. 8AB000-8ABFFF (base 16) EMAC, Inc. 2390 EMAC Way @@ -18313,3 +18766,93 @@ BD4000-BD4FFF (base 16) YUYAMA MFG Co.,Ltd 7F, No. 143, Sec. 3, Cheng Gong Road,, Neihu District Taipei Taiwan 114 TW + +70-B3-D5 (hex) Sikom AS +237000-237FFF (base 16) Sikom AS + Neptunvegen 6 + Verdal 7652 + NO + +70-B3-D5 (hex) Cobo, Inc. +EAD000-EADFFF (base 16) Cobo, Inc. + 3624 Alta Vista Avenue + Santa Rosa CA 95409 + US + +70-B3-D5 (hex) ICT BUSINESS GROUP of Humanrights Center for disabled people +B20000-B20FFF (base 16) ICT BUSINESS GROUP of Humanrights Center for disabled people + #A1101-6,70 Gyeongin-ro 71-gil, + Seoul Yeongdeungpo-gu 07286 + KR + +70-B3-D5 (hex) MULTIVOICE LLC +B06000-B06FFF (base 16) MULTIVOICE LLC + 224 S. Main St #401 + Springville UT 84660 + US + +70-B3-D5 (hex) EMDEP CENTRO TECNOLOGICO MEXICO +83A000-83AFFF (base 16) EMDEP CENTRO TECNOLOGICO MEXICO + Circuito Corral de Piedras #36, Polígono Empresarial San Miguel + San Miguel de Allende Guanajuato 37880 + MX + +70-B3-D5 (hex) OCEANCCTV LTD +FD5000-FD5FFF (base 16) OCEANCCTV LTD + 4F., No. 1, Ln. 297, Xinyi Rd.,Banqiao Dist., + New Taipei City 220 + TW + +70-B3-D5 (hex) Cetto Industries +B73000-B73FFF (base 16) Cetto Industries + Dechenstr. 9-15 + Ratingen 40878 + DE + +70-B3-D5 (hex) Xiris Automation Inc. +9BF000-9BFFFF (base 16) Xiris Automation Inc. + 1016 Sutton Dr, Unit C5 + Burlington Ontario L7L 6B8 + CA + +70-B3-D5 (hex) wtec GmbH +ABD000-ABDFFF (base 16) wtec GmbH + Dornbachstrasse 1a + Bad Homburg 61352 + DE + +70-B3-D5 (hex) LG Electronics +884000-884FFF (base 16) LG Electronics + 19, Yangje-daero 11gil, Seocho-gu + Seoul 06772 + KR + +70-B3-D5 (hex) Prolan Zrt. +C04000-C04FFF (base 16) Prolan Zrt. + Szentendrei út 1-3. + Budakalasz 2011 + HU + +70-B3-D5 (hex) Morgan Schaffer Inc. +1F7000-1F7FFF (base 16) Morgan Schaffer Inc. + 8300 rue St-Patrick bureau 150 + LaSalle Quebec H8N 2H1 + CA + +70-B3-D5 (hex) Season Electronics Ltd +65E000-65EFFF (base 16) Season Electronics Ltd + 600 Nest Business Park + Havant Hampshire PO9 5TL + GB + +70-B3-D5 (hex) Beijing PanGu Company +E54000-E54FFF (base 16) Beijing PanGu Company + Beijing agricultural college + Beijing 100010 + CN + +70-B3-D5 (hex) Innotas Elektronik GmbH +C51000-C51FFF (base 16) Innotas Elektronik GmbH + Rathenaustr. 18a + Zittau D-02763 + DE diff --git a/hwdb/pci.ids b/hwdb/pci.ids index c986f78e0..2b4fe96c0 100644 --- a/hwdb/pci.ids +++ b/hwdb/pci.ids @@ -1,8 +1,8 @@ # # List of PCI ID's # -# Version: 2019.02.13 -# Date: 2019-02-13 03:15:01 +# Version: 2019.04.06 +# Date: 2019-04-06 03:15:02 # # Maintained by Albert Pool, Martin Mares, and other volunteers from # the PCI ID Project at https://pci-ids.ucw.cz/. @@ -219,8 +219,7 @@ 0eac SHF Communication Technologies AG 0008 Ethernet Powerlink Managing Node 01 0f62 Acrox Technologies Co., Ltd. -# Formerly NCR -1000 LSI Logic / Symbios Logic +1000 Broadcom / LSI 0001 53c810 1000 1000 LSI53C810AE PCI to SCSI I/O Processor 0002 53c820 @@ -266,6 +265,7 @@ 1000 1000 LSI53C875A PCI to Ultra SCSI Controller 0014 MegaRAID Tri-Mode SAS3516 1028 1fd4 PERC H745P MX + 1137 020e UCSC-RAID-M5 12G Modular RAID Controller 1d49 0602 ThinkSystem RAID 930-16i 4GB Flash PCIe 12Gb Adapter 1d49 0604 ThinkSystem RAID 930-8e 4GB Flash PCIe 12Gb Adapter 1d49 0607 ThinkSystem RAID 930-16i 8GB Flash PCIe 12Gb Adapter @@ -400,7 +400,7 @@ 8086 3510 RMS25PB080 RAID Controller 8086 3511 RMS25PB040 RAID Controller 8086 3512 RMT3PB080 RAID Controller - 8086 3513 RMS25CB080 RAID Controller + 8086 3513 Integrated RAID Module RMS25CB080 8086 3514 RMS25CB040 RAID Controller 8086 351c RMS25PB080N RAID Controller 8086 351d RMS25CB080N RAID Controller @@ -479,7 +479,9 @@ 0062 SAS1078 PCI-Express Fusion-MPT SAS 1000 0062 SAS1078 PCI-Express Fusion-MPT SAS 0064 SAS2116 PCI-Express Fusion-MPT SAS-2 [Meteor] + 1000 3030 9200-16e 6Gb/s SAS/SATA PCIe x8 External HBA 1000 30c0 SAS 9201-16i + 1000 30d0 9201-16e 6Gb/s SAS/SATA PCIe x8 External HBA 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] @@ -502,6 +504,7 @@ 1000 9241 MegaRAID SAS 9240-4i 1000 92a0 MegaRAID SAS 9220-8i 1014 03b1 ServeRAID M1015 SAS/SATA Controller + 1014 040d ServeRAID M1115 SAS/SATA Controller 1028 1f4e PERC H310 Adapter 1028 1f4f PERC H310 Integrated 1028 1f50 PERC H310 Mini Blades @@ -821,7 +824,6 @@ 1636 Renoir 1714 BeaverCreek HDMI Audio [Radeon HD 6500D and 6400G-6600G series] 103c 168b ProBook 4535s - 2191 TU116M 3150 RV380/M24 [Mobility Radeon X600] 103c 0934 nx8220 3151 RV380 GL [FireMV 2400] @@ -2450,11 +2452,13 @@ 1462 3413 Radeon RX 480 Gaming X 8GB 1462 3416 Radeon RX 570 1462 3418 Radeon RX 580 Armor 4G OC + 1462 8a92 Radeon RX 580 148c 2372 Radeon RX 480 148c 2373 Radeon RX 470 1682 9470 Radeon RX 470 1682 9480 Radeon RX 480 1682 9588 Radeon RX 580 XTR + 1682 c570 Radeon RX 570 174b e347 Radeon RX 470/480 174b e349 Radeon RX 470 1787 a470 Radeon RX 470 @@ -2479,6 +2483,7 @@ 67e9 Baffin [Polaris11] 67eb Baffin [Radeon Pro V5300X] 67ef Baffin [Radeon RX 460/560D / Pro 450/455/460/555/555X/560/560X] + 103c 3421 Radeon RX 460 106b 0160 Radeon Pro 460 106b 0166 Radeon Pro 455 106b 0167 Radeon Pro 450 @@ -2717,6 +2722,8 @@ 6868 Vega 10 [Radeon PRO WX 8100/8200] 686c Vega 10 [Radeon Instinct MI25 MxGPU] 687f Vega 10 XL/XT [Radeon RX Vega 56/64] + 1002 0b36 RX Vega64 + 1002 6b76 RX Vega56 6880 Lexington [Radeon HD 6550M] 103c 163c Pavilion dv6 Radeon HD 6550M 6888 Cypress XT [FirePro V8800] @@ -3608,16 +3615,16 @@ 9613 RS780MC [Mobility Radeon HD 3100] 9614 RS780D [Radeon HD 3300] 9616 RS780L [Radeon 3000] - 9640 BeaverCreek [Radeon HD 6550D] - 9641 BeaverCreek [Radeon HD 6620G] - 9642 Sumo [Radeon HD 6370D] - 9643 Sumo [Radeon HD 6380G] - 9644 Sumo [Radeon HD 6410D] - 9645 Sumo [Radeon HD 6410D] - 9647 BeaverCreek [Radeon HD 6520G] + 9640 Sumo [Radeon HD 6550D] + 9641 Sumo [Radeon HD 6620G] + 9642 SuperSumo [Radeon HD 6370D] + 9643 SuperSumo [Radeon HD 6380G] + 9644 SuperSumo [Radeon HD 6410D] + 9645 SuperSumo [Radeon HD 6410D] + 9647 Sumo [Radeon HD 6520G] 9648 Sumo [Radeon HD 6480G] - 9649 Sumo [Radeon HD 6480G] - 964a BeaverCreek [Radeon HD 6530D] + 9649 SuperSumo [Radeon HD 6480G] + 964a Sumo [Radeon HD 6530D] 964b Sumo 964c Sumo 964e Sumo @@ -3772,6 +3779,7 @@ 174b aa98 Radeon HD 6450 1GB DDR3 aaa0 Tahiti HDMI Audio [Radeon HD 7870 XT / 7950/7970] aab0 Oland/Hainan/Cape Verde/Pitcairn HDMI Audio [Radeon HD 7000 Series] + aab8 Tiran HDMI Audio aac0 Tobago HDMI Audio [Radeon R7 360 / R9 360 OEM] aac8 Hawaii HDMI Audio [Radeon R9 290/290X / 390/390X] aad8 Tonga HDMI Audio [Radeon R9 285/380] @@ -4380,7 +4388,7 @@ 1434 Liverpool Processor SPLL Configuration 1436 Liverpool Processor Root Complex 1437 Liverpool I/O Memory Management Unit - 1438 Liverpool Processor Root Port + 1438 Liverpool UMI PCIe Dummy Host Bridge 1439 Family 16h Processor Functions 5:1 143a Kingston/Clayton/Gladius/Montego Root Complex 143b Kingston/Clayton/Gladius/Montego P2P Bridge for UMI Link @@ -4413,7 +4421,7 @@ 145c Family 17h (Models 00h-0fh) USB 3.0 Host Controller 145d Zeppelin Switch Upstream (PCIE SW.US) 145e Zeppelin Switch Downstream (PCIE SW.DS) - 145f USB 3.0 Host controller + 145f Zeppelin USB 3.0 Host controller 1460 Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 0 1461 Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 1 1462 Family 17h (Models 00h-0fh) Data Fabric: Device 18h; Function 2 @@ -6639,6 +6647,7 @@ 1645 Broadcom NetXtreme BCM5701 Gigabit Ethernet 1801 T2 Bridge Controller 1802 T2 Secure Enclave Processor + 1803 Apple Audio Device 2001 S1X NVMe Controller 2002 S3ELab NVMe Controller 2003 S3X NVMe Controller @@ -6714,14 +6723,17 @@ 1077 02a7 QL45212-DE 25GbE Adapter 1077 e4f6 FastLinQ QL45211H 25GbE Adapter 1077 e4f7 FastLinQ QL45212H 25GbE Adapter + 1590 0245 10/20/25GbE 2P 4820c CNA 165c FastLinQ QL45000 Series 10/25/40/50GbE Controller (FCoE) 1077 0034 QL45262 Flex 50Gb 2-port Ethernet Adapter w/ iSCSI/FCoE 1077 e4f1 FastLinQ QL45462H 40GbE FCoE Adapter 1077 e4f2 FastLinQ QL45461H 40GbE FCoE Adapter + 1590 0245 10/20/25GbE 2P 4820c CNA FCoE 165e FastLinQ QL45000 Series 10/25/40/50GbE Controller (iSCSI) 1077 0034 QL45262 Flex 50Gb 2-port Ethernet Adapter w/ iSCSI/FCoE 1077 e4f1 FastLinQ QL45462H 40GbE iSCSI Adapter 1077 e4f2 FastLinQ QL45461H 40GbE iSCSI Adapter + 1590 0245 10/20/25GbE 2P 4820c CNA iSCSI 1664 FastLinQ QL45000 Series Gigabit Ethernet Controller (SR-IOV VF) 1077 e4f1 FastLinQ QL45462H 40GbE Adapter (SR-IOV VF) 1077 e4f2 FastLinQ QL45461H 40GbE Adapter (SR-IOV VF) @@ -6730,6 +6742,7 @@ 1077 e4f6 FastLinQ QL45211H 25GbE Adapter (SR-IOV VF) 1077 e4f7 FastLinQ QL45212H 25GbE Adapter (SR-IOV VF) 1077 e4f8 FastLinQ QL45611H 100GbE Adapter (SR-IOV VF) + 1590 0245 10/20/25GbE 2P 4820c CNA SRIOV 2020 ISP2020A Fast!SCSI Basic Adapter 2031 ISP8324-based 16Gb Fibre Channel to PCI Express Adapter 103c 17e7 SN1000Q 16Gb Single Port Fibre Channel Adapter @@ -6741,6 +6754,9 @@ 1077 029e QLE2694 Quad Port 16Gb Fibre Channel to PCIe Adapter 1077 02a2 QLE2694L Quad Port 16Gb Fibre Channel to PCIe Adapter 1077 02ad QLE2694U Quad Port 16/32Gb Fibre Channel to PCIe Adapter + 2081 ISP2814-based 64/32G Fibre Channel to PCIe Controller + 1077 02e1 QLE2874 Quad Port 64GFC PCIe Gen4 x16 Adapter + 1077 02e3 QLE2774 Quad Port 32GFC PCIe Gen4 x16 Adapter 2100 QLA2100 64-bit Fibre Channel Adapter 1077 0001 QLA2100 64-bit Fibre Channel Adapter 2200 QLA2200 64-bit Fibre Channel Adapter @@ -6760,6 +6776,11 @@ 1590 00fa StoreFabric SN1100Q 16Gb Dual Port Fibre Channel Host Bus Adapter 1590 0203 StoreFabric SN1600Q 32Gb Single Port Fibre Channel Host Bus Adapter 1590 0204 StoreFabric SN1600Q 32Gb Dual Port Fibre Channel Host Bus Adapter + 2281 ISP2812-based 64/32G Fibre Channel to PCIe Controller + 1077 02e2 QLE2872 Dual Port 64GFC PCIe Gen4 x8 Adapter + 1077 02e4 QLE2772 Dual Port 32GFC PCIe Gen4 x8 Adapter + 1077 02ee QLE2870 Single Port 64GFC PCIe Gen4 x8 Adapter + 1077 02f0 QLE2770 Single Port 32GFC PCIe Gen4 x8 Adapter 2300 QLA2300 64-bit Fibre Channel Adapter 2312 ISP2312-based 2Gb Fibre Channel to PCI-X HBA 103c 0131 2Gb Fibre Channel - Single port [A7538A] @@ -9219,6 +9240,7 @@ 0111 NV11 [GeForce2 MX200] 0112 NV11M [GeForce2 Go] 0113 NV11GL [Quadro2 MXR/EX/Go] + 1028 00e5 Quadro2 Go 0140 NV43 [GeForce 6600 GT] 1458 3125 GV-NX66T128D 1458 3126 GV-NX66T256DE @@ -10130,6 +10152,7 @@ 06ca GF100M [GeForce GTX 480M] 06cb GF100 [GeForce GTX 480] 06cd GF100 [GeForce GTX 470] + 06d0 GF100GL 06d1 GF100GL [Tesla C2050 / C2070] 10de 0771 Tesla C2050 10de 0772 Tesla C2070 @@ -10823,7 +10846,7 @@ 1022 GK110GL [Tesla K20c] 1023 GK110BGL [Tesla K40m] 10de 097e 12GB Computational Accelerator - 1024 GK110BGL [Tesla K40c] + 1024 GK180GL [Tesla K40c] 1026 GK110GL [Tesla K20s] 1027 GK110BGL [Tesla K40st] 1028 GK110GL [Tesla K20m] @@ -11670,7 +11693,7 @@ 1eb0 TU104GL [Quadro RTX 5000] 1eb1 TU104GL [Quadro RTX 4000] 1eb8 TU104GL [Tesla T4] - 1ed0 TU104M [GeForce RTX 2080 Mobile] + 1ed0 TU104BM [GeForce RTX 2080 Mobile] 1f02 TU106 [GeForce RTX 2070] 1043 8673 TURBO RTX 2070 1f04 TU106 @@ -11679,17 +11702,18 @@ 1f10 TU106M [GeForce RTX 2070 Mobile] 1f11 TU106M [GeForce RTX 2060 Mobile] 1f2e TU106M - 1f50 TU106M [GeForce RTX 2070 Mobile] - 1f51 TU106M [GeForce RTX 2060 Mobile] - 1f82 TU107 - 1f92 TU107M + 1f50 TU106BM [GeForce RTX 2070 Mobile] + 1f51 TU106BM [GeForce RTX 2060 Mobile] + 1f82 TU107 [GeForce GTX 1650] + 1f92 TU107M [GeForce GTX 1650 Mobile] 1fbf TU107GL - 2182 TU116 [GeForce GTX 1660 Ti Rev. A] + 2182 TU116 [GeForce GTX 1660 Ti] 2183 TU116 2184 TU116 [GeForce GTX 1660] - 2191 TU116M + 2191 TU116M [GeForce GTX 1660 Mobile] 21ae TU116GL 21bf TU116GL + 21d1 TU116BM [GeForce GTX 1660 Mobile] 10df Emulex Corporation 0720 OneConnect NIC (Skyhawk) 103c 1934 FlexFabric 20Gb 2-port 650M Adapter @@ -11872,6 +11896,7 @@ 103c 1985 Pavilion 17-e163sg Notebook PC 17aa 3832 Yoga 520 522a RTS522A PCI Express Card Reader + 103c 8079 EliteBook 840 G3 5249 RTS5249 PCI Express Card Reader 103c 1909 ZBook 15 524a RTS524A PCI Express Card Reader @@ -11880,6 +11905,7 @@ 1028 06dc Latitude E7470 1028 06e4 XPS 15 9550 17aa 224f ThinkPad X1 Carbon 5th Gen + 5260 RTS5260 PCI Express Card Reader 5286 RTS5286 PCI Express Card Reader 5287 RTL8411B PCI Express Card Reader 5288 RTS5288 PCI Express Card Reader @@ -13179,6 +13205,7 @@ 112f Dalsa Inc. 0000 MVC IC-PCI 0001 MVC IM-PCI Video frame grabber/processor + 0004 PCDig Digital Image Capture 0008 PC-CamLink PCI framegrabber 1130 Computervision 1131 Philips Semiconductors @@ -14560,6 +14587,7 @@ 7820 MV78200 [Discovery Innovation] ARM SoC 7823 MV78230 [Armada XP] ARM SoC 7846 88F6820 [Armada 385] ARM SoC + d40f Bobcat3 Ethernet Switch f003 GT-64010 Primary Image Piranha Image Generator 11ac Canon Information Systems Research Aust. 11ad Lite-On Communications Inc @@ -18429,6 +18457,7 @@ 1590 0211 Ethernet 25Gb 2-port 631FLR-SFP28 Adapter 16d8 BCM57416 NetXtreme-E Dual-Media 10G RDMA Ethernet Controller 1028 1feb NetXtreme-E 10Gb SFP+ Adapter + 14e4 4163 BCM957416M4163C OCP 2x10GBT Type1 wRoCE 1590 020c Ethernet 10Gb 2-port 535T Adapter 1590 0212 Ethernet 10Gb 2-port 535FLR-T Adapter 16d9 BCM57417 NetXtreme-E 10GBASE-T RDMA Ethernet Controller @@ -19408,8 +19437,12 @@ 0211 MT416842 Family [BlueField SoC Flash Recovery] 0212 MT2892 Family [ConnectX-6 Dx Flash Recovery] 0213 MT2892 Family [ConnectX-6 Dx Secure Flash Recovery] + 0214 MT42822 Family [BlueField-2 SoC Flash Recovery] + 0215 MT42822 Family [BlueField-2 Secure Flash Recovery] 024e MT53100 [Spectrum-2, Flash recovery mode] 024f MT53100 [Spectrum-2, Secure Flash recovery mode] + 0250 Spectrum-3, Flash recovery mode + 0251 Spectrum-3, Secure Flash recovery mode 0262 MT27710 [ConnectX-4 Lx Programmable] EN 0263 MT27710 [ConnectX-4 Lx Programmable Virtual Function] EN 0264 Innova-2 Flex Burn image @@ -19490,12 +19523,13 @@ 101b MT28908 Family [ConnectX-6] 101c MT28908 Family [ConnectX-6 Virtual Function] 101d MT2892 Family [ConnectX-6 Dx] - 101e MT2892 Family [ConnectX-6 Dx Virtual Function] + 101e ConnectX Family mlx5Gen Virtual Function 101f MT28851 1020 MT28860 1021 MT28861 1974 MT28800 Family [ConnectX-5 PCIe Bridge] 1975 MT416842 Family [BlueField SoC PCIe Bridge] + 1978 MT42822 Family [BlueField-2 SoC PCIe Bridge] 4117 MT27712A0-FDCF-AE 1bd4 0039 SN10XMP2P25 1bd4 004d SN10XMP2P25,YZPC-01191-101 @@ -19505,6 +19539,7 @@ 5a46 MT23108 PCI Bridge 5e8c MT24204 [InfiniHost III Lx HCA] 5e8d MT25204 [InfiniHost III Lx HCA Flash Recovery] + 6001 NVMe SNAP Controller 6274 MT25204 [InfiniHost III Lx HCA] 6278 MT25208 InfiniHost III Ex (Tavor compatibility mode) 6279 MT25208 [InfiniHost III Ex HCA Flash Recovery] @@ -19545,6 +19580,11 @@ a2d1 MT416842 BlueField SoC Crypto disabled a2d2 MT416842 BlueField integrated ConnectX-5 network controller a2d3 MT416842 BlueField multicore SoC family VF + a2d4 MT42822 BlueField-2 SoC Crypto enabled + a2d5 MT42822 BlueField-2 SoC Crypto disabled + a2d6 MT42822 BlueField-2 integrated ConnectX-6 Dx network controller + c2d2 MT416842 BlueField SoC management interfac + c2d3 MT42822 BlueField-2 SoC Management Interface # SwitchX-2, 40GbE switch c738 MT51136 c739 MT51136 GW @@ -19555,6 +19595,7 @@ cb84 MT52100 cf08 MT53236 cf6c MT53100 [Spectrum-2] + cf70 Spectrum-3 d2f0 Quantum HDR (200Gbps) switch 15b4 CCI/TRIAD 15b5 Cimetrics Inc @@ -20653,6 +20694,8 @@ 1915 Arria10 PCIe MainRef Design [DNPCIe_80G_A10_LL] 1916 VirtexUS PCIe Accelerator Board [DNVUF2_HPC_PCIe] 1917 UltrascalePlus PCIe Accelerator Board [DNPCIe_400G_VU_LL] + 1918 VirtexUS+ ASIC Emulation Board [DNVUPF4A] + 1919 UltrascalePlus PCIe Accelerator Board [DNPCIe_400G_VUP_HBM_LL] 1a00 Virtex6 PCIe DMA Netlist Design 1a01 Virtex6 PCIe Darklite Design [DNPCIe_HXT_10G_LL] 1a02 Virtex7 PCIe DMA Netlist Design @@ -20665,6 +20708,8 @@ 1a0a VirtexUS PCIe Darklite Design [DNVUF2_HPC_PCIe] 1a0b UltrascalePlus PCIe Darklite Design [DNPCIe_400G_VU_LL] 1a0c KintexUS PCIe DRAM Packet Capture Design [DNPCIe_40G_KU_LL] + 1a0d KintexUS PCIe DRAM Packet Capture Design [DNPCIe_40G_KU_LL_2QSFP] + 1a0e UltrascalePlus PCIe Darklite Design [DNPCIe_400G_VUP_HBM_LL] 17e4 Sectra AB 0001 KK671 Cardbus encryption board 0002 KK672 Cardbus encryption board @@ -20990,6 +21035,7 @@ 0175 NT20E3-2-PTP Network Adapter 2x10Gb 0185 NT40A01 Network Adapter 01a5 NT200A01 Network Adapter + 01c5 NT200A02 Network Adapter 18f6 NextIO 1000 [Nexsis] Switch Virtual P2P PCIe Bridge 1001 [Texsis] Switch Virtual P2P PCIe Bridge @@ -21161,7 +21207,13 @@ 1924 801d x2522-R1 2000 Series 10/25G Adapter 1924 801e x2542-R1 2000 Series 40/100G Adapter 1924 8022 XtremeScale X2522 10G Network Adapter + 1924 8024 XtremeScale X2562 OCP 3.0 Dual Port SFP28 + 1924 8027 XtremeScale X2541 PCIe Single Port QSFP28 1924 8028 XtremeScale X2522-25G Network Adapter + 1924 802a XtremeScale X2542 PCIe Dual Port QSFP28 + 1924 802b XtremeScale X2552 OCP 2.0 Dual Port SFP28 + 1924 802c XtremeScale X2522-25G PCIe Dual Port SFP28 + 1924 802d XtremeScale X2562 OCP 3.0 Dual Port SFP28 1803 SFC9020 10G Ethernet Controller (Virtual Function) 1813 SFL9021 10GBASE-T Ethernet Controller (Virtual Function) 1903 SFC9120 10G Ethernet Controller (Virtual Function) @@ -21835,6 +21887,7 @@ 1028 1fd6 BOSS-S1 Adapter 1028 1fdf BOSS-S1 Modular 1028 1fe2 BOSS-S1 Adapter + 1028 2010 BOSS-S2 Adapter 1d49 0300 ThinkSystem M.2 with Mirroring Enablement Kit 9235 88SE9235 PCIe 2.0 x2 4-port SATA 6 Gb/s Controller 9445 88SE9445 PCIe 2.0 x4 4-Port SAS/SATA 6 Gbps RAID Controller @@ -21940,6 +21993,7 @@ 0011 MIPS SoC PCI Express Port 1bf4 VTI Instruments Corporation 0001 SentinelEX + 7011 RX0xxx 1bfd EeeTOP 1c09 CSP, Inc. 4254 10G-PCIE3-8D-2S @@ -21958,7 +22012,8 @@ 0001 82C101 1c28 Lite-On IT Corp. / Plextor 0122 M6e PCI Express SSD [Marvell 88SS9183] -1c2c Fiberblaze +# previously Fiberblaze +1c2c Silicom Denmark 000a Capture 000f SmartNIC 00a0 FBC4G Capture 4x1Gb @@ -21969,9 +22024,15 @@ 00a5 FBC2XLG Capture 2x40Gb 00a6 FBC1CG Capture 1x100Gb 00a9 FBC2XGHH Capture 2x10Gb - 00ad FBC2CGG3HL Capture 2x200Gb + 00ad FBC2CGG3HL Capture 2x100Gb [Padua] 00af Capture slave device - a001 FBC2CGG3 Capture 2x200Gb + 00e0 PacketMover 2x100Gb [Savona] + 00e1 PacketMover 2x100Gb [Tivoli] + a001 FBC2CGG3 Capture 2x100Gb [Mango] + a00e FB2CG Capture 2x100Gb [Savona] + a00f FB2CG Capture 2x40Gb [Savona] + a011 FB2CG Capture 2x25Gb [Savona] + a012 FB2CG Capture 8x10Gb [Savona] # Used on V120 VME Crate Controller 1c32 Highland Technology, Inc. 1c33 Daktronics, Inc @@ -22011,6 +22072,8 @@ 1cb5 Focusrite Audio Engineering Ltd 0002 Clarett 1cb8 Dawning Information Industry Co., Ltd. +1cc4 Union Memory (Shenzhen) + 17ab NVMe 256G SSD device 1cc5 Embedded Intelligence, Inc. 0100 CAN-PCIe-02 1cc7 Radian Memory Systems Inc. @@ -22163,6 +22226,7 @@ 0100 RK3399 PCI Express Root Port 1808 RK1808 Neural Network Processor Card 1d8f Enyx +1d93 YADRO (KNS Group) 1d94 Chengdu Haiguang IC Design Co., Ltd. 1450 Root Complex 1451 I/O Memory Management Unit @@ -22196,8 +22260,13 @@ 1d95 Graphcore Ltd 0001 Colossus GC2 [C2] 0002 Colossus GC1 [S1] +1d9b Facebook, Inc. + 0010 Networking DOM Engine + 0011 IO Bridge 1da1 Teko Telecom S.r.l. 1da2 Sapphire Technology Limited +1da3 Habana Labs Ltd. + 0001 HL-1000 AI Inference Accelerator [Goya] 1dbb NGD Systems, Inc. 1dbf Guizhou Huaxintong Semiconductor Technology Co., Ltd 0401 StarDragon4800 PCI Express Root Port @@ -22227,6 +22296,7 @@ 1df3 0001 ENA2050F 1df3 0002 ENA2050FS 0203 ACE-NIC100 Programmable Network Accelerator + 1df3 0000 Maintenance Mode 1df3 0001 ENA2080F 1df3 0002 ENA2080FS 1df3 0003 ENA2100F @@ -22244,6 +22314,7 @@ 0215 Acorn CLE-215 021f Acorn CLE-215+ 1525 Xilinx BCU-1525 +1e26 Fujitsu Client Computing Limited 1e38 Thinci, Inc 1e3d Burlywood, Inc # nee Tumsan Oy @@ -23644,6 +23715,7 @@ 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 4879 NVMe PCIe 3.0 SSD v2 6.4TB AIC (P4618) 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) @@ -23756,6 +23828,8 @@ 0d16 Crystal Well Integrated Graphics Controller 0d26 Crystal Well Integrated Graphics Controller 0d36 Crystal Well Integrated Graphics Controller + 0d4e Ethernet Connection (10) I219-LM + 0d4f Ethernet Connection (10) I219-V 0d58 Ethernet Controller XXV710 Intel(R) FPGA Programmable Acceleration Card N3000 for Networking 8086 0000 Ethernet Controller XXV710 Intel(R) FPGA Programmable Acceleration Card N3000 for Networking 8086 0001 Ethernet Controller XXV710 Intel(R) FPGA Programmable Acceleration Card N3000 for Networking @@ -24437,6 +24511,7 @@ 103c 2159 Ethernet 10Gb 2-port 562i Adapter 108e 7b11 Ethernet Server Adapter X520-2 1170 004c 82599 DP 10G Mezzanine Adapter + 15d9 0611 AOC-STGN-I2S [REV 1.01] 1734 11a9 10 Gigabit Dual Port Network Connection 17aa 1071 ThinkServer X520-2 AnyFabric 17aa 4007 82599ES 10-Gigabit SFI/SFP+ Network Connection @@ -24908,6 +24983,7 @@ 156d DSL5520 Thunderbolt 2 Bridge [Falcon Ridge 4C 2013] 156f Ethernet Connection I219-LM 1028 06dc Latitude E7470 + 103c 8079 EliteBook 840 G3 1570 Ethernet Connection I219-V 1571 Ethernet Virtual Function 700 Series 1572 Ethernet Controller X710 for 10GbE SFP+ @@ -25021,6 +25097,7 @@ 8086 0007 Ethernet Network Adapter OCP XXV710-1 8086 0008 Ethernet Network Adapter OCP XXV710-1 8086 0009 Ethernet 25G 2P XXV710 Adapter + 8086 000a Ethernet 25G 2P XXV710 OCP 8086 4001 Ethernet Network Adapter XXV710-2 1591 Ethernet Controller E810-C for backplane 1592 Ethernet Controller E810-C for QSFP @@ -25102,6 +25179,11 @@ 15f0 JHL7540 Thunderbolt 3 USB Controller [Titan Ridge DD 2018] 15f6 I210 Gigabit Ethernet Connection 15ff Ethernet Controller X710 for 10GBASE-T + 8086 0000 Ethernet Network Adapter X710-TL + 8086 0001 Ethernet Network Adapter X710-T4L + 8086 0002 Ethernet Network Adapter X710-T4L + 8086 0003 Ethernet Network Adapter X710-T2L + 8086 0004 Ethernet Network Adapter X710-T2L 8086 0005 Ethernet 10G 2P X710-T2L-t Adapter 8086 0006 Ethernet 10G 4P X710-T4L-t Adapter 8086 0007 Ethernet 10G 2P X710-T2L-t OCP @@ -25155,6 +25237,7 @@ 1904 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor Host Bridge/DRAM Registers 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 17aa 382a B51-80 Laptop 1905 Xeon E3-1200 v5/E3-1500 v5/6th Gen Core Processor PCIe Controller (x8) 1906 HD Graphics 510 @@ -25173,6 +25256,7 @@ 1916 Skylake GT2 [HD Graphics 520] 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 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 @@ -25733,6 +25817,7 @@ 2086 Sky Lake-E PCU Registers 208d Sky Lake-E CHA Registers 208e Sky Lake-E CHA Registers + 2241 Larrabee 2250 Xeon Phi coprocessor 5100 series 225c Xeon Phi coprocessor SE10/7120 series 225d Xeon Phi coprocessor 3120 series @@ -26980,6 +27065,7 @@ 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) + 1028 200a Express Flash NVMe [Optane] 375GB 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 @@ -28717,6 +28803,7 @@ 372c Xeon C5500/C3500 Reserved 373f Xeon C5500/C3500 IOxAPIC 37c8 C62x Chipset QuickAssist Technology + 37cc Ethernet Connection X722 37cd Ethernet Virtual Function 700 Series 37ce Ethernet Connection X722 for 10GbE backplane 1590 0215 Ethernet 10Gb 2-port 568i Adapter @@ -29126,8 +29213,10 @@ 3e91 8th Gen Core Processor Gaussian Mixture Model 3e92 UHD Graphics 630 (Desktop) 3e93 UHD Graphics 610 + 3e98 UHD Graphics 630 (Desktop 9 Series) 3e9b UHD Graphics 630 (Mobile) 3ea0 UHD Graphics 620 (Whiskey Lake) + 1028 089e Inspiron 5482 3ea5 Iris Plus Graphics 655 3ec2 8th Gen Core Processor Host Bridge/DRAM Registers 3ec4 8th Gen Core Processor Host Bridge/DRAM Registers @@ -29646,6 +29735,7 @@ 71a1 440GX - 82443GX AGP bridge 71a2 440GX - 82443GX Host bridge (AGP disabled) 4c53 1000 CC7/CR7/CP7/VC7/VP7/VR7 mainboard + 7360 XMM7360 LTE Advanced Modem 7600 82372FB PIIX5 ISA 7601 82372FB PIIX5 IDE 7602 82372FB PIIX5 USB @@ -30051,10 +30141,13 @@ 9d03 Sunrise Point-LP SATA Controller [AHCI mode] 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 17aa 225d ThinkPad T480 17aa 382a B51-80 Laptop 9d10 Sunrise Point-LP PCI Express Root Port #1 + 9d11 Sunrise Point-LP PCI Express Root Port #2 9d12 Sunrise Point-LP PCI Express Root Port #3 + 9d13 Sunrise Point-LP PCI Express Root Port #4 9d14 Sunrise Point-LP PCI Express Root Port #5 17aa 382a B51-80 Laptop 9d15 Sunrise Point-LP PCI Express Root Port #6 @@ -30064,15 +30157,18 @@ 9d18 Sunrise Point-LP PCI Express Root Port #9 17aa 382a B51-80 Laptop 9d19 Sunrise Point-LP PCI Express Root Port #10 + 9d1a Sunrise Point-LP PCI Express Root Port #11 9d21 Sunrise Point-LP PMC 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 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 + 103c 8079 EliteBook 840 G3 17aa 2247 ThinkPad T570 17aa 224f ThinkPad X1 Carbon 5th Gen 17aa 225d ThinkPad T480 @@ -30085,12 +30181,14 @@ 9d2f Sunrise Point-LP USB 3.0 xHCI Controller 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 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 + 103c 8079 EliteBook 840 G3 17aa 2247 ThinkPad T570 17aa 224f ThinkPad X1 Carbon 5th Gen 17aa 225d ThinkPad T480 @@ -30099,16 +30197,19 @@ 9d3a Sunrise Point-LP CSME HECI #1 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 17aa 2247 ThinkPad T570 17aa 224f ThinkPad X1 Carbon 5th Gen 17aa 225d ThinkPad T480 17aa 382a B51-80 Laptop 9d3d Sunrise Point-LP Active Management Technology - SOL + 103c 8079 EliteBook 840 G3 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 + 103c 8079 EliteBook 840 G3 9d4e Sunrise Point LPC Controller/eSPI Controller 17aa 225d ThinkPad T480 9d50 Sunrise Point LPC Controller @@ -30118,6 +30219,7 @@ 17aa 224f ThinkPad X1 Carbon 5th Gen 9d60 Sunrise Point-LP Serial IO I2C Controller #0 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 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 @@ -30129,23 +30231,33 @@ 9d70 Sunrise Point-LP HD Audio 1028 06dc Latitude E7470 1028 06f3 Latitude 3570 + 103c 8079 EliteBook 840 G3 17aa 382a B51-80 Laptop 9d71 Sunrise Point-LP HD Audio 17aa 225d ThinkPad T480 9d84 Cannon Point-LP LPC Controller + 1028 089e Inspiron 5482 9da3 Cannon Point-LP SMBus Controller 9da4 Cannon Point-LP SPI Controller 9db0 Cannon Point-LP PCI Express Root Port #9 + 9db4 Cannon Point-LP PCI Express Root Port #13 + 1028 089e Inspiron 5482 9db6 Cannon Point-LP PCI Express Root Port #15 9db8 Cannon Point-LP PCI Express Root Port #1 9dbc Cannon Point-LP PCI Express Root Port #5 9dc8 Cannon Point-LP High Definition Audio Controller + 1028 089e Inspiron 5482 9dd3 Cannon Point-LP SATA Controller [AHCI Mode] 9de0 Cannon Point-LP MEI Controller #1 + 9de8 Cannon Point-LP Serial IO I2C Controller #0 + 1028 089e Inspiron 5482 + 9de9 Cannon Point-LP Serial IO I2C Controller #1 + 1028 089e Inspiron 5482 9ded Cannon Point-LP USB 3.1 xHCI Controller 9def Cannon Point-LP Shared SRAM 9df0 Cannon Point-LP CNVi [Wireless-AC] 9df9 Cannon Point-LP Thermal Controller + 9dfc Cannon Point-LP Integrated Sensor Hub a000 Atom Processor D4xx/D5xx/N4xx/N5xx DMI Bridge 1458 5000 GA-D525TUD 8086 4f4d DeskTop Board D510MO @@ -30466,6 +30578,13 @@ d158 Core Processor Miscellaneous Registers f1a5 SSD 600P Series f1a6 SSD Pro 7600p/760p/E 6100p Series + f1a8 SSDPEKNW020T8 [660p, 2TB] + 8086 390d SSDPEKNW020T8 [660p, 2TB] +8088 Beijing Wangxun Technology Co., Ltd. + 1001 Ethernet Controller RP1000 for 10GbE SFP+ + 8088 0000 Ethernet Network Adaptor RP1000 for 10GbE SFP+ + 2001 Ethernet Controller RP2000 for 10GbE SFP+ + 8088 2000 Ethernet Network Adaptor RP2000 for 10GbE SFP+ 80ee InnoTek Systemberatung GmbH beef VirtualBox Graphics Adapter cafe VirtualBox Guest Service @@ -30808,11 +30927,34 @@ 103c 0701 Smart Array P204i-b SR Gen10 103c 1100 Smart Array P816i-a SR Gen10 103c 1101 Smart Array P416ie-m SR G10 + 105b 1211 HBA 8238-16i + 105b 1321 HBA 8242-24i + 13fe 8312 SKY-9200 MIC-8312BridgeB 152d 8a22 QS-8204-8i 152d 8a23 QS-8238-16i 152d 8a24 QS-8236-16i 152d 8a36 QS-8240-24i 152d 8a37 QS-8242-24i + 193d 8460 HBA H460-M1 + 193d 8461 HBA H460-B1 + 193d c460 RAID P460-M2 + 193d c461 RAID P460-B2 + 193d f460 RAID P460-M4 + 193d f461 RAID P460-B4 + 19e5 d227 SmartROC-HD SR465C-M 4G + 19e5 d228 SmartROC SR455C-M 2G + 19e5 d229 SmartIOC SR155-M + 19e5 d22a SmartIOC-HD SR765-M + 19e5 d22b SmartROC-e SR455C-ME 4G + 19e5 d22c SmartROC SR455C-M 4G + 1bd4 0045 SMART-HBA 8242-24i + 1bd4 0046 RAID 8236-16i + 1bd4 0047 RAID 8240-24i + 1bd4 0048 SMART-HBA 8238-16i + 1bd4 004a PM8222-SHBA + 1bd4 004b RAID PM8204-2GB + 1bd4 004c RAID PM8204-4GB + 1bd4 004f PM8222-HBA 9005 0608 SmartRAID 3162-8i /e 9005 0800 SmartRAID 3154-8i 9005 0801 SmartRAID 3152-8i @@ -31039,6 +31181,7 @@ bdbd Blackmagic Design a144 DeckLink Mini Monitor 4K a148 DeckLink SDI Micro a14b DeckLink 8K Pro + a14e DeckLink Quad HDMI Recorder a1ff eGPU RX580 c001 TSI Telsys c0a9 Micron/Crucial Technology @@ -31277,6 +31420,7 @@ f1d0 AJA Video db09 Corvid 24 dcaf Kona HD dfee Xena HD-DA + eb0d Corvid 88 eb0e Corvid 44 eb1d Kona 5 efac Xena SD-MM/SD-22-MM diff --git a/hwdb/usb.ids b/hwdb/usb.ids index a27679a21..0f2177459 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: 2019.01.17 -# Date: 2019-01-17 20:34:05 +# Version: 2019.03.20 +# Date: 2019-03-20 20:34:05 # # Vendors, devices and interfaces. Please keep sorted. @@ -169,6 +169,7 @@ 7617 AT76C505AS Wireless Adapter 7800 Mini Album 800c Airspy HF+ + ff02 WootingTwo ff07 Tux Droid fish dongle 03ec Iwatsu America, Inc. 03ed Mitel Corp. @@ -190,6 +191,7 @@ 0012 DeskJet 1125C Printer Port 0024 KU-0316 Keyboard 002a LaserJet P1102 + 0053 DeskJet 2620 All-in-One Printer 0101 ScanJet 4100c 0102 PhotoSmart S20 0104 DeskJet 880c/970c @@ -244,6 +246,7 @@ 0512 DeckJet 450 0517 LaserJet 1000 051d Bluetooth Interface + 052a LaserJet M1212nf MFP 0601 ScanJet 6300c 0604 DeskJet 840c 0605 ScanJet 2200c @@ -1289,6 +1292,7 @@ 0419 Samsung Info. Systems America, Inc. 0001 IrDA Remote Controller / Creative Cordless Mouse 0600 Desktop Wireless 6000 + 2694 Laila 3001 Xerox P1202 Laser Printer 3003 Olivetti PG L12L 3201 Docuprint P8ex @@ -1610,7 +1614,6 @@ 2228 9-in-2 Card Reader 223a 8-in-1 Card Reader 2503 USB 2.0 Hub - 2504 USB 2.0 Hub 2507 hub 2512 USB 2.0 Hub 2513 2.0 Hub @@ -1622,6 +1625,7 @@ 2660 Hub 2744 Hub 274d HTC Hub Controller + 2807 Hub 3fcc RME MADIface 4041 Hub and media card controller 4060 Ultra Fast Media Reader @@ -1629,6 +1633,7 @@ 5434 Hub 5534 Hub 5744 Hub + 5807 Hub 7500 LAN7500 Ethernet 10/100/1000 Adapter 9500 LAN9500/LAN9500i 9512 SMC9512/9514 USB Hub @@ -2404,6 +2409,7 @@ 0799 Surface Pro embedded keyboard 07a5 Wireless Receiver 1461C 07b2 2.4GHz Transceiver v8.0 used by mouse Wireless Desktop 900 + 07b6 Comfort Curve Keyboard 3000 07b9 Wired Keyboard 200 07c6 RTL8153 GigE [Surface Dock Ethernet] 07ca Surface Pro 3 Docking Station Audio Device @@ -3200,6 +3206,7 @@ 0100 Stor.E Slim USB 3.0 0200 External Disk 0820 Canvio Advance Disk + 0821 Canvio Advance 2TB model DTC920 a006 External Disk 1.5TB a007 External Disk USB 3.0 a009 Stor.E Basics @@ -3262,6 +3269,7 @@ 5730 Audio Speaker 5731 Microphone 5740 Virtual COM Port + 5750 LED badge -- mini LED display -- 11x44 7270 ST Micro Serial Bridge 7554 56k SoftModem 8213 ThermaData Logger Cradle @@ -3306,6 +3314,7 @@ 9503 ITE it9503 feature-limited DVB-T transmission chip [ccHDtv] 9507 ITE it9507 full featured DVB-T transmission chip [ccHDtv] 9910 IT9910 chipset based grabber + ff59 Hdmi-CEC Bridge 048f Eicon Tech. 0490 United Microelectronics Corp. 0491 Capetronic @@ -3373,6 +3382,7 @@ 1054 S90XS Keyboard/Music Synthesizer 160f P-105 1613 Clavinova CLP535 + 1617 PSR-E353 digital keyboard 1704 Steinberg UR44 2000 DGP-7 2001 DGP-5 @@ -4293,6 +4303,7 @@ 0428 D7000 0429 D5100 042a D800 (ptp) + 0430 D7100 043f D5600 0f03 PD-10 Wireless Printer Adapter 4000 Coolscan LS 40 ED @@ -4869,6 +4880,8 @@ 9015 ICD 4 In-Circuit Debugger c001 PicoLCD 20x4 e11c TL866CS EEPROM Programmer [MiniPRO] + edb4 micro PLC (ATSAMD51G19A) [Black Brix ECU II] + edb5 ATMEGA32U4 [Black Brix ECU] f2c4 Macareux-labs Hygrometry Temperature Sensor f2f7 Yepkit YKUSH f3aa Macareux-labs Usbce Bootloader mode @@ -5176,6 +5189,7 @@ 3426 SCX-4500 Laser Printer 342d SCX-4x28 Series 344f SCX-3400 Series + 347e C48x Series Color Laser Multifunction Printer 3605 InkJet Color Printer 3606 InkJet Color Printer 3609 InkJet Color Printer @@ -5255,6 +5269,7 @@ 6125 D3 Station External Hard Drive 61b5 M3 Portable Hard Drive 2TB 61b6 M3 Portable Hard Drive 1TB + 61b7 M3 Portable Hard Drive 4TB 61f3 Portable SSD T3 (MU-PT250B, MU-PT500B) 61f5 Portable SSD T5 6601 Mobile Phone @@ -5317,6 +5332,7 @@ 7061 eHome Infrared Receiver 7080 Anycall SCH-W580 7081 Human Interface Device + 7301 Fingerprint Device 8001 Handheld d003 GT-I9003 e020 SERI E02 SCOM 6200 UMTS Phone @@ -5459,6 +5475,7 @@ 02f4 2.4G Cordless Mouse 0381 Touchscreen 04a0 Dream Cheeky Stress/Panic Button + 2234 Touchscreen 04f4 Harting Elektronik, Inc. 04f5 Fujitsu-ICL Systems, Inc. 04f6 Norand Corp. @@ -5971,6 +5988,9 @@ 2041 PT-2730 P-touch Label Printer 2061 PT-P700 P-touch Label Printer 2064 PT-P700 P-touch Label Printer RemovableDisk + 209b QL-800 P-touch Label Printer + 209c QL-810W P-touch Label Printer + 209d QL-820NWB P-touch Label Printer 2100 Card Reader Writer 2102 Sewing machine 60a0 ADS-2000 @@ -6387,6 +6407,7 @@ 003c VAIO-MX LCD Control 0045 Digital Imaging Video 0046 Network Walkman + 0049 UP-D895 004a Memory Stick Hi-Fi System 004b Memory Stick Reader/Writer 004e DSC-xxx (ptp) @@ -6471,6 +6492,7 @@ 01d0 DVD+RW External Drive DRU-700A 01d5 IC RECORDER 01de VRD-VC10 [Video Capture] + 01e7 UP-D897 01e8 UP-DR150 Photo Printer 01e9 Net MD 01ea Hi-MD WALKMAN @@ -6760,6 +6782,8 @@ 0037 PL700 0038 PL510 0039 DTU-710 + 003a DTI-520 + 003b Integrated Hub 003f DTZ-2100 [Cintiq 21UX] 0041 XD-0405-U [Intuos2 (4x5)] 0042 XD-0608-U [Intuos2 (6x8)] @@ -6782,7 +6806,7 @@ 006a CTE-460 [Bamboo One Pen (S)] 006b CTE-660 [Bamboo One Pen (M)] 0081 CTE-630BT [Graphire Wireless (6x8)] - 0084 Wireless adapter for Bamboo tablets + 0084 ACK-40401 [Wireless Accessory Kit] 0090 TPC90 0093 TPC93 0097 TPC97 @@ -6828,6 +6852,7 @@ 00ec TPCEC 00ed TPCED 00ef TPCEF + 00f0 DTU-1631 00f4 DTK-2400 [Cintiq 24HD] tablet 00f6 DTH-2400 [Cintiq 24HD touch] touchscreen 00f8 DTH-2400 [Cintiq 24HD touch] tablet @@ -6853,7 +6878,21 @@ 0315 PTH-651 [Intuos pro (M)] 0317 PTH-851 [Intuos pro (L)] 0318 CTH-301 [Bamboo] + 0319 CTH-300 [Bamboo Pad wireless] + 0323 CTL-680 [Intuos Pen (M)] + 032a DTK-2700 [Cintiq 27QHD] + 032b DTH-2700 [Cintiq 27QHD touch] tablet + 032c DTH-2700 [Cintiq 27QHD touch] touchscreen 032f DTU-1031X + 0331 ACK-411050 [ExpressKey Remote] + 0333 DTH-1300 [Cintiq 13HD Touch] tablet + 0335 DTH-1300 [Cintiq 13HD Touch] touchscreen + 0336 DTU-1141 + 033b CTL-490 [Intuos Draw (S)] + 033c CTH-490 [Intuos Art/Photo/Comic (S)] + 033d CTL-690 [Intuos Draw (M)] + 033e CTH-690 [Intuos Art (M)] + 0343 DTK-1651 0347 Integrated Hub 0348 Integrated Hub 034a DTH-W1320 [MobileStudio Pro 13] touchscreen @@ -6870,6 +6909,7 @@ 0356 DTH-3220 [Cintiq Pro 32] touchscreen 0357 PTH-660 [Intuos Pro (M)] 0358 PTH-860 [Intuos Pro (L)] + 0359 DTU-1141B 035a DTH-1152 tablet 0368 DTH-1152 touchscreen 0374 CTL-4100 [Intuos (S)] @@ -6920,6 +6960,9 @@ 0074 Optical mouse M-FW1UL 0075 Laser mouse M-FW2DL 0077 Laser mouse M-LY2UL + 0079 Laser mouse M-D21DL + 007b Laser mouse M-D20DR + 007c Laser Bluetooth mouse M-BT5BL 2003 JC-U3613M 2004 JC-U3613M 200c LD-USB/TX @@ -9351,6 +9394,7 @@ 3020 Hercules Webcam EC300 a300 Dual Analog Leader GamePad b000 Hercules DJ Console + b121 Hercules P32 DJ c000 Hercules Muse Pocket d002 Hercules DJ Console e000 HWGUSB2-54 WLAN @@ -12064,6 +12108,9 @@ 0070 NRP-Z57 0083 NRP-Z85 0095 NRP-Z86 + 0117 HMF / HMP / HMS-X / HMO series Oscilloscopes + 0118 HMF / HMP / HMS-X / HMO series Oscilloscopes + 0119 HMF / HMP / HMS-X / HMO series Oscilloscopes 0aae NEC infrontia Corp. (Nitsuko) 0aaf Digitalway Co., Ltd 0ab0 Arrow Strong Electronics Co., Ltd @@ -19051,6 +19098,9 @@ 22b9 eTurboTouch Technology, Inc. 0006 Touch Screen 22ba Technology Innovation Holdings, Ltd +22e0 secunet Security Networks AG + 0002 SINA Flash Drive + 0003 SINA ID Token A 2304 Pinnacle Systems, Inc. 0109 Studio PCTV USB (SECAM) 0110 Studio PCTV USB (PAL) @@ -19187,6 +19237,21 @@ 1213 MediaTV Pro III MiniPCIe (US) 2676 Basler AG ba02 ace +2717 Xiaomi Inc. + 0011 100Mbps Network Card Adapter + 0360 Mi3W + 0368 Mi4 LTE + 3801 Mi ANC & Type-C In-Ear Earphones + 4106 MediaTek MT7601U [MI WiFi] + ff08 Redmi Note 3 (ADB Interface) + ff10 Mi/Redmi series (PTP) + ff18 Mi/Redmi series (PTP + ADB) + ff40 Mi/Redmi series (MTP) + ff48 Mi/Redmi series (MTP + ADB) + ff60 redmi prime 2 + ff68 Mi-4c + ff80 Mi/Redmi series (RNDIS) + ff88 Mi/Redmi series (RNDIS + ADB) 2730 Citizen 200f CT-S310 Label printer 2735 DigitalWay diff --git a/man/binfmt.d.xml b/man/binfmt.d.xml index a4e508251..8caeb56a5 100644 --- a/man/binfmt.d.xml +++ b/man/binfmt.d.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/bootctl.xml b/man/bootctl.xml index 9cfa9cccd..a80a37309 100644 --- a/man/bootctl.xml +++ b/man/bootctl.xml @@ -1,10 +1,7 @@ - - - + @@ -46,18 +43,34 @@ - - Path to the EFI System Partition (ESP). If not specified, /efi, - /boot, and /boot/efi are checked in turn. It is recommended to mount - the ESP to /boot, if possible. + + Path to the EFI System Partition (ESP). If not specified, /efi/, + /boot/, and /boot/efi are checked in turn. It is recommended to mount + the ESP to /efi/, if possible. + + + + + Path to the Extended Boot Loader partition, as defined in the Boot Loader Specification. If not + specified, /boot/ are checked. It is recommended to mount the Extended Boot + Loader partition to /boot/, if possible. - - This option modifies the behaviour of status. - Just print the path to the EFI System Partition (ESP) to standard output and - exit. + + This option modifies the behaviour of status. Prints only the + path to the EFI System Partition (ESP) to standard output and exits. + + + + + This option modifies the behaviour of status. Prints only the + path to the Extended Boot Loader partition if it exists, and the path to the ESP otherwise to + standard output and exit. This command is useful to determine where to place boot loader entries, as + they are preferably placed in the Extended Boot Loader partition if it exists and in the ESP + otherwise. @@ -87,10 +100,10 @@ - Installs systemd-boot into the EFI system partition. A copy of systemd-boot - will be stored as the EFI default/fallback loader at - ESP/EFI/BOOT/BOOT*.EFI. The boot loader is then added to the - top of the firmware's boot loader list. + Installs systemd-boot into the EFI system partition. A copy of + systemd-boot will be stored as the EFI default/fallback loader at + ESP/EFI/BOOT/BOOT*.EFI. The boot loader is then added + to the top of the firmware's boot loader list. @@ -138,9 +151,12 @@ Environment - If $SYSTEMD_RELAX_ESP_CHECKS=1 is set the validation checks for the ESP are relaxed, and - the path specified with may refer to any kind of file system on any kind of - partition. + If $SYSTEMD_RELAX_ESP_CHECKS=1 is set the validation checks for the ESP are + relaxed, and the path specified with may refer to any kind of file system on + any kind of partition. + + Similarly, $SYSTEMD_RELAX_XBOOTLDR_CHECKS=1 turns off some validation checks for + the Extended Boot Loader partition. diff --git a/man/bootup.xml b/man/bootup.xml index 5f63fe778..9468a6131 100644 --- a/man/bootup.xml +++ b/man/bootup.xml @@ -1,10 +1,7 @@ - - - + @@ -26,22 +23,29 @@ Description - A number of different components are involved in the system - boot. Immediately after power-up, the system BIOS will do minimal - hardware initialization, and hand control over to a boot loader - stored on a persistent storage device. This boot loader will then - invoke an OS kernel from disk (or the network). In the Linux case, - this kernel (optionally) extracts and executes an initial RAM disk - image (initrd), such as generated by + A number of different components are involved in the boot of a Linux system. Immediately after + power-up, the system firmware will do minimal hardware initialization, and hand control over to a boot + loader (e.g. + systemd-boot7 or + GRUB) stored on a persistent storage device. This + boot loader will then invoke an OS kernel from disk (or the network). On systems using EFI or other types + of firmware, this firmware may also load the kernel directly. + + The kernel (optionally) mounts an in-memory file system, often generated by dracut8, - which looks for the root file system (possibly using - systemd1 - for this). After the root file system is found and mounted, the - initrd hands over control to the host's system manager (such as - systemd1) - stored on the OS image, which is then responsible for probing all - remaining hardware, mounting all necessary file systems and - spawning all configured services. + which looks for the root file system. Nowadays this is usually implemented as an initramfs — a compressed + archive which is extracted when the kernel boots up into a lightweight in-memory file system based on + tmpfs, but in the past normal file systems using an in-memory block device (ramdisk) were used, and the + name "initrd" is still used to describe both concepts. It's the boot loader or the firmware that loads + both the kernel and initrd/initramfs images into memory, but the kernel which interprets it as a file + system. systemd1 may + be used to manage services in the initrd, similarly to the real system. + + After the root file system is found and mounted, the initrd hands over control to the host's system + manager (such as + systemd1) stored in + the root file system, which is then responsible for probing all remaining hardware, mounting all + necessary file systems and spawning all configured services. On shutdown, the system manager stops all services, unmounts all file systems (detaching the storage technologies backing diff --git a/man/busctl.xml b/man/busctl.xml index a5e3d92cf..e4c7fcb28 100644 --- a/man/busctl.xml +++ b/man/busctl.xml @@ -1,10 +1,7 @@ - - - + + @@ -187,7 +184,7 @@ BOOL - When used with the call command, specifies + When used with the call or emit command, specifies whether the method call should implicitly activate the called service, should it not be running yet but is configured to be auto-started. Defaults to @@ -247,6 +244,15 @@ + + SERVICE + + + Takes a service name. When used with the emit command, a signal is + emitted to the specified service. + + + @@ -341,6 +347,15 @@ option. + + emit OBJECT INTERFACE SIGNAL SIGNATURE ARGUMENT + + Emit a signal. Takes a object path, interface name and method name. If parameters + shall be passed, a signature string is required, followed by the arguments, individually formatted as + strings. For details on the formatting used, see below. To specify the destination of the signal, + use the option. + + get-property SERVICE OBJECT INTERFACE PROPERTY diff --git a/man/coredump.conf.xml b/man/coredump.conf.xml index 4ccc174bb..81cdc3389 100644 --- a/man/coredump.conf.xml +++ b/man/coredump.conf.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/coredumpctl.xml b/man/coredumpctl.xml index 94d5626fb..5419bc151 100644 --- a/man/coredumpctl.xml +++ b/man/coredumpctl.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/crypttab.xml b/man/crypttab.xml index 3574ce00d..5eb1c1223 100644 --- a/man/crypttab.xml +++ b/man/crypttab.xml @@ -1,6 +1,7 @@ - + - - - + @@ -77,9 +74,10 @@ setsid() to detach from any terminal and create an independent session. - In the child, call fork() - again, to ensure that the daemon can never re-acquire a - terminal again. + In the child, call fork() again, to ensure that the daemon can + never re-acquire a terminal again. (This relevant if the program — and all its dependencies — does + not carefully specify `O_NOCTTY` on each and every single `open()` call that might potentially open a + TTY device node.) Call exit() in the first child, so that only the second child (the actual daemon @@ -244,6 +242,10 @@ and systemd.exec5. + As new-style daemons are invoked without a controlling TTY (but as their own session + leaders) care should be taken to always specify `O_NOCTTY` on `open()` calls that possibly reference + a TTY device node, so that no controlling TTY is accidentally acquired. + These recommendations are similar but not identical to the diff --git a/man/dnssec-trust-anchors.d.xml b/man/dnssec-trust-anchors.d.xml index d5faee291..8b6394e92 100644 --- a/man/dnssec-trust-anchors.d.xml +++ b/man/dnssec-trust-anchors.d.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/environment.d.xml b/man/environment.d.xml index 2257dcb04..702154837 100644 --- a/man/environment.d.xml +++ b/man/environment.d.xml @@ -1,6 +1,7 @@ - + - - - + @@ -128,24 +125,22 @@ /tmp/ - The place for small temporary files. This - directory is usually mounted as a tmpfs - instance, and should hence not be used for larger files. (Use - /var/tmp/ for larger files.) Since the - directory is accessible to other users of the system, it is - essential that this directory is only written to with the - mkstemp3, - mkdtemp3 - and related calls. This directory is usually flushed at - boot-up. Also, files that are not accessed within a certain - time are usually automatically deleted. If applications find - the environment variable $TMPDIR set, they - should prefer using the directory specified in it over - directly referencing /tmp/ (see - environ7 - and + The place for small temporary files. This directory is usually mounted as a + tmpfs instance, and should hence not be used for larger files. (Use + /var/tmp/ for larger files.) Since the directory is accessible to other users of + the system, it is essential that this directory is only written to with the mkstemp3, + mkdtemp3 and + related calls. This directory is usually flushed at boot-up. Also, files that are not accessed within + a certain time are usually automatically deleted. If applications find the environment variable + $TMPDIR set, they should prefer using the directory specified in it over directly + referencing /tmp/ (see environ7 and IEEE - Std 1003.1 for details). + Std 1003.1 for details). For further details about this directory, see Using /tmp/ And /var/tmp/ + Safely. @@ -337,24 +332,22 @@ /var/tmp/ - The place for larger and persistent temporary - files. In contrast to /tmp/, this directory - is usually mounted from a persistent physical file system and - can thus accept larger files. (Use /tmp/ - for smaller files.) This directory is generally not flushed at - boot-up, but time-based cleanup of files that have not been - accessed for a certain time is applied. The same security - restrictions as with /tmp/ apply, and - hence only - mkstemp3, - mkdtemp3 - or similar calls should be used to make use of this directory. - If applications find the environment variable - $TMPDIR set, they should prefer using the - directory specified in it over directly referencing - /var/tmp/ (see - environ7 - for details). + The place for larger and persistent temporary files. In contrast to + /tmp/, this directory is usually mounted from a persistent physical file system + and can thus accept larger files. (Use /tmp/ for smaller files.) This directory + is generally not flushed at boot-up, but time-based cleanup of files that have not been accessed for + a certain time is applied. The same security restrictions as with /tmp/ apply, + and hence only mkstemp3, + mkdtemp3 or + similar calls should be used to make use of this directory. If applications find the environment + variable $TMPDIR set, they should prefer using the directory specified in it over + directly referencing /var/tmp/ (see environ7 for + details). For further details about this directory, see Using /tmp/ And /var/tmp/ + Safely. diff --git a/man/halt.xml b/man/halt.xml index 19857cea8..75604b834 100644 --- a/man/halt.xml +++ b/man/halt.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/hostname.xml b/man/hostname.xml index de33a7478..bb880c336 100644 --- a/man/hostname.xml +++ b/man/hostname.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/hostnamectl.xml b/man/hostnamectl.xml index 013f1267a..ddbeb7e7e 100644 --- a/man/hostnamectl.xml +++ b/man/hostnamectl.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/hwdb.xml b/man/hwdb.xml index 7d550c6d7..7ba523602 100644 --- a/man/hwdb.xml +++ b/man/hwdb.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/journal-remote.conf.xml b/man/journal-remote.conf.xml index 8fa557c0b..ab3dfd011 100644 --- a/man/journal-remote.conf.xml +++ b/man/journal-remote.conf.xml @@ -1,5 +1,5 @@ - - - - + diff --git a/man/journalctl.xml b/man/journalctl.xml index 7ff0a479e..a3c67f5e8 100644 --- a/man/journalctl.xml +++ b/man/journalctl.xml @@ -1,10 +1,7 @@ - - - + + @@ -464,8 +461,8 @@ - - + + Show messages from a specific boot. This will add a match for _BOOT_ID=. @@ -497,6 +494,10 @@ offset is not specified, a value of zero is assumed, and the logs for the boot given by ID are shown. + + The special argument all can be + used to negate the effect of an earlier use of + . @@ -618,6 +619,17 @@ journal specified by the passed cursor. + + + + If FILE exists and contains a + cursor, start showing entries after this location. + Otherwise the show entries according the other given options. At the end, + write the cursor of the last entry to FILE. Use + this option to continually read the journal by sequentially calling + journalctl. + + diff --git a/man/journald.conf.xml b/man/journald.conf.xml index 2791678a4..57c225656 100644 --- a/man/journald.conf.xml +++ b/man/journald.conf.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/kernel-command-line.xml b/man/kernel-command-line.xml index 43dfc1073..40b776621 100644 --- a/man/kernel-command-line.xml +++ b/man/kernel-command-line.xml @@ -1,10 +1,7 @@ - - - + @@ -137,10 +134,13 @@ enables fully state-less boots were the vendor-supplied OS is used as shipped, with only default configuration and no stored state in effect, as /etc and /var (as well as all other resources shipped in the root file system) are reset at boot and lost on shutdown. If this - setting is set to state the root file system is mounted as usual, however + setting is set to state the root file system is mounted read-only, however /var is mounted as a volatile memory file system (tmpfs), so that the - system boots up with the normal configuration applied, but all state reset at boot and lost at shutdown. For details, - see + system boots up with the normal configuration applied, but all state reset at boot and lost at shutdown. If + this setting is set to overlay the root file system is set up as + overlayfs mount combining the read-only root directory with a writable + tmpfs, so that no modifications are made to disk, but the file system may be modified + nonetheless with all changes being lost at reboot. For details, see systemd-volatile-root.service8 and systemd-fstab-generator8. @@ -329,7 +329,7 @@ Configures the root file system and its file system type and mount options, as well as whether it shall be - mounted read-only or read-writable initially. For details, + mounted read-only or read-write initially. For details, see systemd-fstab-generator8. diff --git a/man/kernel-install.xml b/man/kernel-install.xml index 50e132010..34bb5d2f1 100644 --- a/man/kernel-install.xml +++ b/man/kernel-install.xml @@ -1,12 +1,10 @@ - + + - - - + kernel-install @@ -27,6 +25,7 @@ kernel-install COMMAND + OPTIONS KERNEL-VERSION KERNEL-IMAGE INITRD-FILE @@ -35,9 +34,9 @@ Description - - kernel-install is used to install and remove kernel and - initramfs images to and from /boot. + kernel-install is used to install and remove kernel and initramfs images to and + from the boot loader partition, referred to as $BOOT here. It will usually be one of + /boot, /efi, or /boot/efi, see below. kernel-install will execute the files @@ -65,17 +64,20 @@ add KERNEL-VERSION KERNEL-IMAGE [INITRD-FILE ...] This command expects a kernel version string and a path to a kernel image file as - arguments. kernel-install creates the directory - /boot/MACHINE-ID/KERNEL-VERSION/ - and calls the executables from /usr/lib/kernel/install.d/*.install and + arguments. kernel-install calls the executables from + /usr/lib/kernel/install.d/*.install and /etc/kernel/install.d/*.install with the following arguments: - add KERNEL-VERSION /boot/MACHINE-ID/KERNEL-VERSION/ KERNEL-IMAGE [INITRD-FILE ...] + add KERNEL-VERSION $BOOT/MACHINE-ID/KERNEL-VERSION/ KERNEL-IMAGE [INITRD-FILE ...] - Two default plugins execute the following operations in this case: + Three default plugins execute the following operations in this case: + 00-entry-directory.install creates the directory + $BOOT/MACHINE-ID/KERNEL-VERSION/ + if $BOOT/MACHINE-ID/ already exists. + 50-depmod.install runs depmod8 for the @@ -83,16 +85,20 @@ 90-loaderentry.install copies KERNEL-IMAGE to - /boot/MACHINE-ID/KERNEL-VERSION/linux. + $BOOT/MACHINE-ID/KERNEL-VERSION/linux. If an INITRD-FILE is provided, it also copies INITRD-FILE to - /boot/MACHINE-ID/KERNEL_VERSION/INITRD-FILE. + $BOOT/MACHINE-ID/KERNEL_VERSION/INITRD-FILE. It also creates a boot loader entry according to the Boot Loader Specification in - /boot/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. + $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. The title of the entry is the PRETTY_NAME parameter specified in /etc/os-release or /usr/lib/os-release (if the former is - missing), or "Linux KERNEL-VERSION", if unset. + missing), or "Linux KERNEL-VERSION", if unset. + + If the entry directory + $BOOT/MACHINE-ID/KERNEL-VERSION/ + does not exist, this plugin does nothing. @@ -103,11 +109,11 @@ /usr/lib/kernel/install.d/*.install and /etc/kernel/install.d/*.install with the following arguments: - remove KERNEL-VERSION /boot/MACHINE-ID/KERNEL-VERSION/ + remove KERNEL-VERSION $BOOT/MACHINE-ID/KERNEL-VERSION/ Afterwards, kernel-install removes the directory - /boot/MACHINE-ID/KERNEL-VERSION/ + $BOOT/MACHINE-ID/KERNEL-VERSION/ and its contents. Two default plugins execute the following operations in this case: @@ -117,14 +123,46 @@ 50-depmod.install removes the files generated by depmod for this kernel again. 90-loaderentry.install removes the file - /boot/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. + $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION.conf. + + + The <varname>$BOOT</varname> partition + The partition where the kernels and Boot + Loader Specification snippets are located is called $BOOT. + kernel-install determines the location of this partition by checking + /efi/, /boot/, and /boot/efi + in turn. The first location where $BOOT/loader/entries/ or + $BOOT/$MACHINE_ID/ exists is used. + + + + Options + The following options are understood: + + + + + + + Output additional information about operations being performed. + + + + + + + + + Environment variables + If is used, $KERNEL_INSTALL_VERBOSE=1 will be set for + the plugins. They may output additional logs in this case. @@ -162,7 +200,7 @@ Read by 90-loaderentry.install. 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 - /boot/loader/entries/MACHINE-ID-KERNEL-VERSION+TRIES.conf. This + $BOOT/loader/entries/MACHINE-ID-KERNEL-VERSION+TRIES.conf. This is useful for boot loaders such as systemd-boot7 which implement boot attempt counting with a counter embedded in the entry file name. diff --git a/man/less-variables.xml b/man/less-variables.xml index eb332b56b..c80534b96 100644 --- a/man/less-variables.xml +++ b/man/less-variables.xml @@ -1,10 +1,7 @@ - - - + + Environment diff --git a/man/libsystemd-pkgconfig.xml b/man/libsystemd-pkgconfig.xml index a177fbe46..e378c4dd7 100644 --- a/man/libsystemd-pkgconfig.xml +++ b/man/libsystemd-pkgconfig.xml @@ -1,10 +1,7 @@ - - - + + Notes diff --git a/man/libudev.xml b/man/libudev.xml index 382c1aa25..c35bc292e 100644 --- a/man/libudev.xml +++ b/man/libudev.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/loader.conf.xml b/man/loader.conf.xml index f9d98dd4d..38a80861b 100644 --- a/man/loader.conf.xml +++ b/man/loader.conf.xml @@ -1,7 +1,6 @@ - - - - - + diff --git a/man/localectl.xml b/man/localectl.xml index 50c7e1325..0752f7838 100644 --- a/man/localectl.xml +++ b/man/localectl.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/localtime.xml b/man/localtime.xml index f51c67f99..0f1652ee2 100644 --- a/man/localtime.xml +++ b/man/localtime.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/loginctl.xml b/man/loginctl.xml index 67eeffda8..7b0745f93 100644 --- a/man/loginctl.xml +++ b/man/loginctl.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/logind.conf.xml b/man/logind.conf.xml index ac8032ab4..4cbfd09cb 100644 --- a/man/logind.conf.xml +++ b/man/logind.conf.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/machine-id.xml b/man/machine-id.xml index 7ed2dda43..f4d94e880 100644 --- a/man/machine-id.xml +++ b/man/machine-id.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/machine-info.xml b/man/machine-info.xml index 5a268c9f3..4fb27abe0 100644 --- a/man/machine-info.xml +++ b/man/machine-info.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/machinectl.xml b/man/machinectl.xml index 95823eb41..357574f3f 100644 --- a/man/machinectl.xml +++ b/man/machinectl.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/meson.build b/man/meson.build index 05197d6ef..ae9c941fc 100644 --- a/man/meson.build +++ b/man/meson.build @@ -198,12 +198,9 @@ if git.found() custom_target( 'update-man-rules', output : 'update-man-rules', - # slightly strange syntax because of - # https://github.com/mesonbuild/meson/issues/1643 - # and https://github.com/mesonbuild/meson/issues/1512 command : ['sh', '-c', 'cd @0@ && '.format(meson.build_root()) + - 'python3 @0@/tools/make-man-rules.py `git ls-files ":/man/*.xml"` >t && '.format(meson.source_root()) + + 'python3 @0@/tools/make-man-rules.py $(git ls-files ":/man/*.xml") >t && '.format(project_source_root) + 'mv t @0@/rules/meson.build'.format(meson.current_source_dir())], depend_files : custom_entities_ent) endif diff --git a/man/modules-load.d.xml b/man/modules-load.d.xml index 57c4c6807..d126634e7 100644 --- a/man/modules-load.d.xml +++ b/man/modules-load.d.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/networkctl.xml b/man/networkctl.xml index 7877755ed..6c28c4bb2 100644 --- a/man/networkctl.xml +++ b/man/networkctl.xml @@ -1,10 +1,7 @@ - - - + + @@ -78,11 +75,13 @@ list - LINK… + PATTERN… - Show a list of existing links and their status. If no further arguments are specified shows all links, + Show a list of existing links and their status. If one ore more + PATTERNs are specified, only links matching one of them are shown. + If no further arguments are specified shows all links, otherwise just the specified links. Produces output similar to: IDX LINK TYPE OPERATIONAL SETUP @@ -113,10 +112,18 @@ the device has a carrier, but is not yet ready for normal traffic + + degraded-carrier + + for bond or bridge master, one of the bonding or bridge slave network interfaces is + in off, no-carrier, or dormant state + + carrier - the link has a carrier + the link has a carrier, or for bond or bridge master, all bonding or bridge slave + network interfaces are enslaved to the master. @@ -125,6 +132,12 @@ the link has carrier and addresses valid on the local link configured + + enslaved + + the link has carrier and is enslaved to bond or bridge master network interface + + routable @@ -180,13 +193,13 @@ status - LINK… + PATTERN… - Show information about the specified links: type, - state, kernel module driver, hardware and IP address, - configured DNS servers, etc. + Show information about the specified links: type, state, kernel module driver, hardware and + IP address, configured DNS servers, etc. If one ore more PATTERNs are + specified, only links matching one of them are shown. When no links are specified, an overall network status is shown. Also see the option . @@ -208,13 +221,14 @@ lldp - LINK… + PATTERN… - Show discovered LLDP (Link Layer Discovery Protocol) neighbors. If one or more link names are specified - only neighbors on those interfaces are shown. Otherwise shows discovered neighbors on all interfaces. Note - that for this feature to work, LLDP= must be turned on for the specific interface, see + Show discovered LLDP (Link Layer Discovery Protocol) neighbors. If one or more + PATTERNs are specified only neighbors on those interfaces are shown. + Otherwise shows discovered neighbors on all interfaces. Note that for this feature to work, + LLDP= must be turned on for the specific interface, see systemd.network5 for details. diff --git a/man/networkd.conf.xml b/man/networkd.conf.xml index c624d4de4..516455416 100644 --- a/man/networkd.conf.xml +++ b/man/networkd.conf.xml @@ -1,5 +1,5 @@ - - - - + diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml index 5742d8977..ed03035e5 100644 --- a/man/nss-mymachines.xml +++ b/man/nss-mymachines.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index 960d58000..e4ea4e189 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml index d3d68437d..8fde11867 100644 --- a/man/nss-systemd.xml +++ b/man/nss-systemd.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/os-release.xml b/man/os-release.xml index 6de0cd714..5a5e318f7 100644 --- a/man/os-release.xml +++ b/man/os-release.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/pam_systemd.xml b/man/pam_systemd.xml index e5e14c12d..fd8e4fb77 100644 --- a/man/pam_systemd.xml +++ b/man/pam_systemd.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/portablectl.xml b/man/portablectl.xml index 3a5517a4a..01f6a1dee 100644 --- a/man/portablectl.xml +++ b/man/portablectl.xml @@ -1,7 +1,6 @@ - - - - - + + diff --git a/man/resolved.conf.xml b/man/resolved.conf.xml index d37bf0d3a..c8ab6942c 100644 --- a/man/resolved.conf.xml +++ b/man/resolved.conf.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/rules/meson.build b/man/rules/meson.build index 0c990a0c5..d949900dd 100644 --- a/man/rules/meson.build +++ b/man/rules/meson.build @@ -275,7 +275,9 @@ manpages = [ ''], ['sd_bus_new', '3', - ['sd_bus_flush_close_unref', + ['sd_bus_close_unref', + 'sd_bus_close_unrefp', + 'sd_bus_flush_close_unref', 'sd_bus_flush_close_unrefp', 'sd_bus_ref', 'sd_bus_unref', diff --git a/man/runlevel.xml b/man/runlevel.xml index 8b5025da4..d8bfcd73f 100644 --- a/man/runlevel.xml +++ b/man/runlevel.xml @@ -1,10 +1,7 @@ - - - + - - - + + diff --git a/man/sd-bus.xml b/man/sd-bus.xml index 4620590be..6c925e316 100644 --- a/man/sd-bus.xml +++ b/man/sd-bus.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd-daemon.xml b/man/sd-daemon.xml index a404213f4..84deda188 100644 --- a/man/sd-daemon.xml +++ b/man/sd-daemon.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd-event.xml b/man/sd-event.xml index 2e6ab69f8..afdafff5d 100644 --- a/man/sd-event.xml +++ b/man/sd-event.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd-id128.xml b/man/sd-id128.xml index 4425c45d1..74d838df2 100644 --- a/man/sd-id128.xml +++ b/man/sd-id128.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd-journal.xml b/man/sd-journal.xml index 3fa6c75b7..a3ee1ea9e 100644 --- a/man/sd-journal.xml +++ b/man/sd-journal.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd-login.xml b/man/sd-login.xml index 374395726..2787bd747 100644 --- a/man/sd-login.xml +++ b/man/sd-login.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_booted.xml b/man/sd_booted.xml index ace5417c2..6a6a62063 100644 --- a/man/sd_booted.xml +++ b/man/sd_booted.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_bus_add_match.xml b/man/sd_bus_add_match.xml index c4f24aed3..a10654438 100644 --- a/man/sd_bus_add_match.xml +++ b/man/sd_bus_add_match.xml @@ -1,6 +1,6 @@ - + + + @@ -87,20 +84,20 @@ 0 or a positive integer. On failure, they return a negative errno-style error code. sd_bus_get_event() returns an event loop object or NULL. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - - + The bus connection has been created in a different process. + + + diff --git a/man/sd_bus_close.xml b/man/sd_bus_close.xml index 369afd874..b09f48887 100644 --- a/man/sd_bus_close.xml +++ b/man/sd_bus_close.xml @@ -1,10 +1,7 @@ - - - + + @@ -69,20 +66,20 @@ On success, sd_bus_flush() returns 0 or a positive integer. On failure, it returns a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - - + The bus connection has been created in a different process. + + + diff --git a/man/sd_bus_creds_get_pid.xml b/man/sd_bus_creds_get_pid.xml index c068fbfb2..9e79d13cd 100644 --- a/man/sd_bus_creds_get_pid.xml +++ b/man/sd_bus_creds_get_pid.xml @@ -1,10 +1,7 @@ - - - + + @@ -458,62 +455,53 @@ On success, these calls return 0 or a positive integer. On failure, these calls return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENODATA + + + -ENODATA - The given field is not available in the - credentials object c. - - + The given field is not available in the credentials object + c. + + - - -ENXIO + + -ENXIO - The given field is not specified for the described - process or peer. This will be returned by - sd_bus_creds_get_unit(), - sd_bus_creds_get_slice(), - sd_bus_creds_get_user_unit(), - sd_bus_creds_get_user_slice(), and - sd_bus_creds_get_session() if the process is - not part of a systemd system unit, systemd user unit, systemd - slice, or logind session. It will be returned by - sd_bus_creds_get_owner_uid() if the process is - not part of a systemd user unit or logind session. It will also be - returned by sd_bus_creds_get_exe() and - sd_bus_creds_get_cmdline() for kernel - threads (since these are not started from an executable binary, - nor have a command line), and by - sd_bus_creds_get_audit_session_id() and - sd_bus_creds_get_audit_login_uid() when - the process is not part of an audit session, and - sd_bus_creds_get_tty() if the process has - no controlling TTY. - - - + The given field is not specified for the described process or peer. This will be + returned by sd_bus_creds_get_unit(), + sd_bus_creds_get_slice(), sd_bus_creds_get_user_unit(), + sd_bus_creds_get_user_slice(), and + sd_bus_creds_get_session() if the process is not part of a systemd system + unit, systemd user unit, systemd slice, or logind session. It will be returned by + sd_bus_creds_get_owner_uid() if the process is not part of a systemd user unit + or logind session. It will also be returned by sd_bus_creds_get_exe() and + sd_bus_creds_get_cmdline() for kernel threads (since these are not started + from an executable binary, nor have a command line), and by + sd_bus_creds_get_audit_session_id() and + sd_bus_creds_get_audit_login_uid() when the process is not part of an audit + session, and sd_bus_creds_get_tty() if the process has no controlling + TTY. + - - -EINVAL + + -EINVAL - Specified pointer parameter is NULL. - - + Specified pointer parameter is NULL. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_creds_new_from_pid.xml b/man/sd_bus_creds_new_from_pid.xml index eda5d17d8..f5ee01ca3 100644 --- a/man/sd_bus_creds_new_from_pid.xml +++ b/man/sd_bus_creds_new_from_pid.xml @@ -1,10 +1,7 @@ - - - + + @@ -264,42 +261,40 @@ not needed anymore, this reference should be destroyed with sd_bus_creds_unref3. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ESRCH + + -ESRCH - Specified pid could not - be found. - + Specified pid could not be found. + - - -EINVAL + + -EINVAL - Specified parameter is invalid - (NULL in case of output - parameters). - + Specified parameter is invalid (NULL in case of output + parameters). + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - + Memory allocation failed. + - - -EOPNOTSUPP + + -EOPNOTSUPP - One of the requested fields is unknown to the local system. - - + One of the requested fields is unknown to the local system. + + + diff --git a/man/sd_bus_default.xml b/man/sd_bus_default.xml index dfb32b9b4..51c27f04f 100644 --- a/man/sd_bus_default.xml +++ b/man/sd_bus_default.xml @@ -1,10 +1,7 @@ - - - + + @@ -272,36 +269,37 @@ On success, these calls return 0 or a positive integer. On failure, these calls return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - The specified parameters are invalid. - + The specified parameters are invalid. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - + Memory allocation failed. + - - -ESOCKTNOSUPPORT + + -ESOCKTNOSUPPORT - The protocol version required to connect to the selected bus is not supported. - - + The protocol version required to connect to the selected bus is not + supported. + + - In addition, any further connection-related errors may be - by returned. See sd_bus_send3. + In addition, any further connection-related errors may be by returned. See + sd_bus_send3. + diff --git a/man/sd_bus_error.xml b/man/sd_bus_error.xml index 36cdf4c33..8616f7ee4 100644 --- a/man/sd_bus_error.xml +++ b/man/sd_bus_error.xml @@ -1,10 +1,7 @@ - - - + + @@ -328,29 +325,28 @@ project='man-pages'>free3 the memory held by the structure itself after freeing its contents with sd_bus_error_free(). - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - Error was already set in - sd_bus_error structure when one the - error-setting functions was called. - + Error was already set in sd_bus_error structure when one + the error-setting functions was called. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_error_add_map.xml b/man/sd_bus_error_add_map.xml index dbe05a189..1dfc7259e 100644 --- a/man/sd_bus_error_add_map.xml +++ b/man/sd_bus_error_add_map.xml @@ -1,10 +1,7 @@ - - - + + @@ -101,27 +98,27 @@ tables. It returns zero when the same array was already added before. On error, a negative errno-style error code is returned. See below for known error codes. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - The specified mapping array is invalid. - + The specified mapping array is invalid. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_get_fd.xml b/man/sd_bus_get_fd.xml index 2a81bddaa..6a022b1a1 100644 --- a/man/sd_bus_get_fd.xml +++ b/man/sd_bus_get_fd.xml @@ -1,6 +1,6 @@ - + + + @@ -71,21 +68,21 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection was created in a different process. - + The bus connection was created in a different process. + - + + diff --git a/man/sd_bus_is_open.xml b/man/sd_bus_is_open.xml index 0388db82a..d993142cb 100644 --- a/man/sd_bus_is_open.xml +++ b/man/sd_bus_is_open.xml @@ -1,10 +1,7 @@ - - - + + @@ -73,20 +70,20 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - - + The bus connection has been created in a different process. + + + diff --git a/man/sd_bus_message_append.xml b/man/sd_bus_message_append.xml index 1fdda51dc..b87468e37 100644 --- a/man/sd_bus_message_append.xml +++ b/man/sd_bus_message_append.xml @@ -1,10 +1,7 @@ - - - + + @@ -230,12 +227,11 @@ sd_bus_message_append(m, "ynqiuxtd", y, n, q, i, u, x, t, d); Return Value - On success, these functions return 0 or a positive - integer. On failure, these functions return a negative + On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - + + diff --git a/man/sd_bus_message_append_array.xml b/man/sd_bus_message_append_array.xml index 746f9e3cc..b9595d6a0 100644 --- a/man/sd_bus_message_append_array.xml +++ b/man/sd_bus_message_append_array.xml @@ -1,10 +1,7 @@ - - - + + @@ -157,11 +154,11 @@ Return Value - On success, these calls return 0 or a positive integer. On - failure, they return a negative errno-style error code. - + On success, these calls return 0 or a positive integer. On failure, they return a negative + errno-style error code. - + + diff --git a/man/sd_bus_message_append_basic.xml b/man/sd_bus_message_append_basic.xml index abd3baef3..a180046b9 100644 --- a/man/sd_bus_message_append_basic.xml +++ b/man/sd_bus_message_append_basic.xml @@ -1,10 +1,7 @@ - - - + + @@ -202,49 +199,49 @@ On success, this call returns 0 or a positive integer. On failure, it returns a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - Specified parameter is invalid. - - + Specified parameter is invalid. + + - - -EPERM + + -EPERM - Message has been sealed. - - + Message has been sealed. + + - - -ESTALE + + -ESTALE - Message is in invalid state. - - + Message is in invalid state. + + - - -ENXIO + + -ENXIO - Message cannot be appended to. - - + Message cannot be appended to. + + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_message_append_string_memfd.xml b/man/sd_bus_message_append_string_memfd.xml index 4224af0a2..c59b8a428 100644 --- a/man/sd_bus_message_append_string_memfd.xml +++ b/man/sd_bus_message_append_string_memfd.xml @@ -1,10 +1,7 @@ - - - + + @@ -100,11 +97,11 @@ Return Value - On success, those calls return 0 or a positive integer. On - failure, they returns a negative errno-style error code. - + On success, those calls return 0 or a positive integer. On failure, they return a negative + errno-style error code. - + + diff --git a/man/sd_bus_message_append_strv.xml b/man/sd_bus_message_append_strv.xml index 5f1a4f567..2ce321633 100644 --- a/man/sd_bus_message_append_strv.xml +++ b/man/sd_bus_message_append_strv.xml @@ -1,10 +1,7 @@ - - - + + @@ -61,11 +58,11 @@ Return Value - On success, this call returns 0 or a positive integer. On - failure, a negative errno-style error code is returned. - + On success, this call returns 0 or a positive integer. On failure, a negative errno-style error + code is returned. - + + diff --git a/man/sd_bus_message_copy.xml b/man/sd_bus_message_copy.xml index ac2a4f32b..cd8ad72e4 100644 --- a/man/sd_bus_message_copy.xml +++ b/man/sd_bus_message_copy.xml @@ -1,10 +1,7 @@ - - - + + @@ -54,50 +51,49 @@ 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. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - source or m are - NULL. - + source or m are + NULL. + - - -EPERM + + -EPERM - Message m has been sealed or - source has not been sealed. - - + Message m has been sealed or source + has not been sealed. + - - -ESTALE + + -ESTALE - Destination message is in invalid state. - - + Destination message is in invalid state. + + - - -ENXIO + + -ENXIO - Destination message cannot be appended to. - - + Destination message cannot be appended to. + + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_message_get_cookie.xml b/man/sd_bus_message_get_cookie.xml index cc9a445c0..7b9408c04 100644 --- a/man/sd_bus_message_get_cookie.xml +++ b/man/sd_bus_message_get_cookie.xml @@ -1,10 +1,7 @@ - - - + @@ -66,37 +63,33 @@ Return Value - On success, these calls return 0 or a positive integer. On - failure, these calls return a negative errno-style error - code. + On success, these calls return 0 or a positive integer. On failure, they return a negative + errno-style error code. - On success, the cookie/reply cookie is returned in the - specified 64-bit unsigned integer variable. - + On success, the cookie/reply cookie is returned in the specified 64-bit unsigned integer + variable. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - A specified parameter - is invalid. - + A specified parameter is invalid. + - - -ENODATA + + -ENODATA - No cookie has been assigned to this message. - This either indicates that the message has not been sent yet - and hence has no cookie assigned, or that the message is not a - method response message and hence carries a reply cookie - field. - - + No cookie has been assigned to this message. This either indicates that the + message has not been sent yet and hence has no cookie assigned, or that the message is not a method + response message and hence carries a reply cookie field. + + + diff --git a/man/sd_bus_message_get_monotonic_usec.xml b/man/sd_bus_message_get_monotonic_usec.xml index 06ea227d8..958b83253 100644 --- a/man/sd_bus_message_get_monotonic_usec.xml +++ b/man/sd_bus_message_get_monotonic_usec.xml @@ -1,10 +1,7 @@ - - - + @@ -101,32 +98,29 @@ On success, the timestamp or sequence number is returned in the specified 64-bit unsigned integer variable. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - A specified parameter is - invalid. - + A specified parameter is invalid. + - - -ENODATA + + -ENODATA - No timestamp or sequence number information is - attached to the passed message. This error is returned if the - underlying transport does not support timestamping or - assigning of sequence numbers, or if this feature has not been - negotiated with - sd_bus_negotiate_timestamp3. - - + No timestamp or sequence number information is attached to the passed message. This + error is returned if the underlying transport does not support timestamping or assigning of + sequence numbers, or if this feature has not been negotiated with + sd_bus_negotiate_timestamp3. + + + diff --git a/man/sd_bus_message_get_signature.xml b/man/sd_bus_message_get_signature.xml index bee677819..e3f41346b 100644 --- a/man/sd_bus_message_get_signature.xml +++ b/man/sd_bus_message_get_signature.xml @@ -1,5 +1,5 @@ - @@ -72,28 +72,28 @@ The other functions return 0 or a positive integer on success. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The message parameter is NULL. - - + The message parameter is NULL. + + - - NULL + + NULL - The message parameter is NULL. - - - + The message parameter is NULL. + + + + diff --git a/man/sd_bus_message_get_type.xml b/man/sd_bus_message_get_type.xml index 6f6b7d0ba..d8a45ce7a 100644 --- a/man/sd_bus_message_get_type.xml +++ b/man/sd_bus_message_get_type.xml @@ -1,5 +1,5 @@ - @@ -96,21 +96,21 @@ On success, those functions return 0 or a positive integer. On failure, it returns a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The message parameter or the output parameter are - NULL. - - + The message parameter or the output parameter are + NULL. + + + diff --git a/man/sd_bus_message_new.xml b/man/sd_bus_message_new.xml index 78bca8a89..efe650dea 100644 --- a/man/sd_bus_message_new.xml +++ b/man/sd_bus_message_new.xml @@ -1,5 +1,5 @@ - @@ -141,33 +141,33 @@ NULL. sd_bus_message_get_bus() always returns the bus object. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - Specified type is invalid. - + Specified type is invalid. + - - -ENOTCONN + + -ENOTCONN - The bus parameter bus is NULL or - the bus is not connected. - + The bus parameter bus is NULL or + the bus is not connected. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_message_new_method_call.xml b/man/sd_bus_message_new_method_call.xml index c643177ba..0d181ed82 100644 --- a/man/sd_bus_message_new_method_call.xml +++ b/man/sd_bus_message_new_method_call.xml @@ -1,6 +1,6 @@ - + This function returns 0 if the message object was successfully created, and a negative errno-style error code otherwise. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The output parameter m is - NULL. + The output parameter m is + NULL. - The destination parameter is non-null and is not a valid D-Bus - service name (org.somewhere.Something), the path - parameter is not a valid D-Bus path (/an/object/path), the - interface parameter is non-null and is not a valid D-Bus interface - name (an.interface.name), or the member parameter - is not a valid D-Bus member (Name). + The destination parameter is non-null and is not a valid D-Bus + service name (org.somewhere.Something), the path + parameter is not a valid D-Bus path (/an/object/path), the + interface parameter is non-null and is not a valid D-Bus interface + name (an.interface.name), or the member parameter + is not a valid D-Bus member (Name). - The call parameter is not a method call object. - - + The call parameter is not a method call object. + + - - -ENOTCONN + + -ENOTCONN - The bus parameter bus is NULL or - the bus is not connected. - + The bus parameter bus is NULL or + the bus is not connected. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - + Memory allocation failed. + - - -EPERM + + -EPERM - - The call parameter is not sealed. - - + + The call parameter is not sealed. + + - - -EOPNOTSUPP + + -EOPNOTSUPP - - The call message does not have a cookie. - - - + + The call message does not have a cookie. + + + + diff --git a/man/sd_bus_message_new_method_error.xml b/man/sd_bus_message_new_method_error.xml index 045c74f21..27cec8ed0 100644 --- a/man/sd_bus_message_new_method_error.xml +++ b/man/sd_bus_message_new_method_error.xml @@ -1,10 +1,7 @@ - - - + + @@ -130,50 +127,50 @@ These functions return 0 if the error reply was successfully created, and a negative errno-style error code otherwise. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The call message call or the output - parameter m are NULL. + The call message call or the output + parameter m are NULL. - Message call is not a method call - message. + Message call is not a method call + message. - The error error parameter to - sd_bus_message_new_method_error is not set, see - sd_bus_error_is_set3. - - - + The error error parameter to + sd_bus_message_new_method_error is not set, see + sd_bus_error_is_set3. + + + - - -EPERM + + -EPERM - Message call has been sealed. - - + Message call has been sealed. + + - - -ENOTCONN + + -ENOTCONN - The bus to which message call is - attached is not connected. - + The bus to which message call is + attached is not connected. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_message_new_signal.xml b/man/sd_bus_message_new_signal.xml index c9ed0bc4a..61619304d 100644 --- a/man/sd_bus_message_new_signal.xml +++ b/man/sd_bus_message_new_signal.xml @@ -1,6 +1,6 @@ - + This function returns 0 if the message object was successfully created, and a negative errno-style error code otherwise. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The output parameter m is - NULL. + The output parameter m is + NULL. - The path parameter is not a valid D-Bus path - (/an/object/path), the interface parameter is not - a valid D-Bus interface name (an.interface.name), or the - member parameter is not a valid D-Bus member - (Name). - + The path parameter is not a valid D-Bus path + (/an/object/path), the interface parameter is not + a valid D-Bus interface name (an.interface.name), or the + member parameter is not a valid D-Bus member + (Name). + - - -ENOTCONN + + -ENOTCONN - The bus parameter bus is NULL or - the bus is not connected. - + The bus parameter bus is NULL or + the bus is not connected. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_message_read.xml b/man/sd_bus_message_read.xml index 2815c0198..526fb0e70 100644 --- a/man/sd_bus_message_read.xml +++ b/man/sd_bus_message_read.xml @@ -1,10 +1,7 @@ - - - + + @@ -157,14 +154,12 @@ Return Value - - On success, sd_bus_message_read() and - sd_bus_message_readv() return 0 or a positive integer. On - failure, they return a negative errno-style error code. - - + On success, sd_bus_message_read() and + sd_bus_message_readv() return 0 or a positive integer. On failure, they return a + negative errno-style error code. - + + diff --git a/man/sd_bus_message_read_array.xml b/man/sd_bus_message_read_array.xml index 31481272b..117afa9e3 100644 --- a/man/sd_bus_message_read_array.xml +++ b/man/sd_bus_message_read_array.xml @@ -1,5 +1,5 @@ - @@ -58,41 +58,41 @@ a positive integer. On failure, it returns a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - Specified type is invalid or the message parameter or one of the output - parameters are NULL. - + Specified type is invalid or the message parameter or one of the output + parameters are NULL. + - - -EOPNOTSUPP + + -EOPNOTSUPP - The byte order in the message is different than native byte - order. - + The byte order in the message is different than native byte + order. + - - -EPERM + + -EPERM - The message is not sealed. - + The message is not sealed. + - - -EBADMSG + + -EBADMSG - The message cannot be parsed. - + The message cannot be parsed. + - + + diff --git a/man/sd_bus_message_read_basic.xml b/man/sd_bus_message_read_basic.xml index 774d3fcf8..101f5d21a 100644 --- a/man/sd_bus_message_read_basic.xml +++ b/man/sd_bus_message_read_basic.xml @@ -1,6 +1,6 @@ - + @@ -51,30 +51,30 @@ 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. + + + Errors + + Returned errors may indicate the following problems: + + + + -EINVAL + + The m parameter is NULL. + + + + -EPERM + + The message m has not been sealed. + + + - - Errors - - Returned errors may indicate the following problems: - - - - -EINVAL - - The m parameter is NULL. - - - - -EPERM - - The message m has not been sealed. - - - - See Also diff --git a/man/sd_bus_message_set_destination.xml b/man/sd_bus_message_set_destination.xml index e8a1206ae..ca3e466d7 100644 --- a/man/sd_bus_message_set_destination.xml +++ b/man/sd_bus_message_set_destination.xml @@ -1,9 +1,6 @@ - - - + @@ -109,35 +106,35 @@ On success, these calls return 0 or a positive integer. On failure, these calls return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The message parameter or the output parameter are - NULL. - + The message parameter or the output parameter are + NULL. + - - -EPERM + + -EPERM - For sd_bus_message_set_destination or - sd_bus_message_set_sender, the message is already - sealed. - + For sd_bus_message_set_destination or + sd_bus_message_set_sender, the message is already + sealed. + - - -EEXIST + + -EEXIST - The message already has a destination or sender field set. - - + The message already has a destination or sender field set. + + + diff --git a/man/sd_bus_message_set_expect_reply.xml b/man/sd_bus_message_set_expect_reply.xml index 2dfabca18..6f22e8205 100644 --- a/man/sd_bus_message_set_expect_reply.xml +++ b/man/sd_bus_message_set_expect_reply.xml @@ -1,5 +1,5 @@ - @@ -84,32 +84,32 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The message parameter is - NULL. - + The message parameter is + NULL. + - - -EPERM + + -EPERM - The message message is sealed - when trying to set a flag. + The message message is sealed + when trying to set a flag. - The message message has wrong - type. - - - + The message message has wrong + type. + + + + diff --git a/man/sd_bus_message_skip.xml b/man/sd_bus_message_skip.xml index 384a79149..0e740ab26 100644 --- a/man/sd_bus_message_skip.xml +++ b/man/sd_bus_message_skip.xml @@ -1,5 +1,5 @@ - @@ -50,46 +50,46 @@ On success, sd_bus_message_skip() returns 0 or a positive integer. On failure, it returns a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The m parameter is - NULL. - + The m parameter is + NULL. + - - -EBADMSG + + -EBADMSG - The message cannot be parsed. - + The message cannot be parsed. + - - -EPERM + + -EPERM - The message is not sealed. - + The message is not sealed. + - - -ENXIO + + -ENXIO - The message end has been reached and the requested elements cannot be read. - - + The message end has been reached and the requested elements cannot be read. + + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_message_verify_type.xml b/man/sd_bus_message_verify_type.xml index fcb9f19e1..c3230e583 100644 --- a/man/sd_bus_message_verify_type.xml +++ b/man/sd_bus_message_verify_type.xml @@ -1,5 +1,5 @@ - @@ -56,32 +56,32 @@ On success, this call returns true if the type matches and zero if not (the message m contains different data or the end of the message has been reached). On failure, it returns a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - m or both type and - contents are NULL. + m or both type and + contents are NULL. - Arguments do not satisfy other contraints listed above. - - + Arguments do not satisfy other contraints listed above. + + - - -EPERM + + -EPERM - Message m is not sealed. - - - + Message m is not sealed. + + + + diff --git a/man/sd_bus_negotiate_fds.xml b/man/sd_bus_negotiate_fds.xml index c8d520ecd..c12b65c98 100644 --- a/man/sd_bus_negotiate_fds.xml +++ b/man/sd_bus_negotiate_fds.xml @@ -1,10 +1,7 @@ - - - + + @@ -111,20 +108,20 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EPERM + + + -EPERM - The bus connection has already been started. - - + The bus connection has already been started. + + + diff --git a/man/sd_bus_new.xml b/man/sd_bus_new.xml index cc08e6be1..8506ee87e 100644 --- a/man/sd_bus_new.xml +++ b/man/sd_bus_new.xml @@ -1,10 +1,7 @@ - - - + + @@ -169,20 +166,20 @@ sd_bus_unref() and sd_bus_flush_close_unref() always return NULL. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_path_encode.xml b/man/sd_bus_path_encode.xml index 03130a697..ade11bc15 100644 --- a/man/sd_bus_path_encode.xml +++ b/man/sd_bus_path_encode.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/sd_bus_process.xml b/man/sd_bus_process.xml index 66f22c29a..8f35388da 100644 --- a/man/sd_bus_process.xml +++ b/man/sd_bus_process.xml @@ -1,6 +1,6 @@ - + + + @@ -94,56 +91,56 @@ These functions return 0 if the error reply was successfully sent or if none was expected, and a negative errno-style error code otherwise. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The call message call is - NULL. + The call message call is + NULL. - Message call is not a method call message. - + Message call is not a method call message. + - Message call is not attached to a bus. + Message call is not attached to a bus. - The error error parameter to - sd_bus_reply_method_error is not set, see - sd_bus_error_is_set3. - - - + The error error parameter to + sd_bus_reply_method_error is not set, see + sd_bus_error_is_set3. + + + - - -EPERM + + -EPERM - Message call has been sealed. - - + Message call has been sealed. + + - - -ENOTCONN + + -ENOTCONN - The bus to which message call is - attached is not connected. - + The bus to which message call is attached is not + connected. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + - In addition, any error message returned by - sd_bus_send1 - may be returned. + In addition, any error message returned by + sd_bus_send1 + may be returned. + diff --git a/man/sd_bus_request_name.xml b/man/sd_bus_request_name.xml index cf0b55211..0f6a4ec31 100644 --- a/man/sd_bus_request_name.xml +++ b/man/sd_bus_request_name.xml @@ -1,10 +1,7 @@ - - - + @@ -135,63 +132,63 @@ case, the caller can subscribe to NameOwnerChanged signals to be notified when the name is successfully acquired. sd_bus_request_name() returns > 0 when the name has immediately been acquired successfully. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EALREADY + + + -EALREADY - The caller already is the owner of the specified name. - + The caller already is the owner of the specified name. + - - -EEXIST + + -EEXIST - The name has already been acquired by a different peer, and SD_BUS_NAME_REPLACE_EXISTING was - not specified or the other peer did not specify SD_BUS_NAME_ALLOW_REPLACEMENT while acquiring the - name. - + The name has already been acquired by a different peer, and SD_BUS_NAME_REPLACE_EXISTING was + not specified or the other peer did not specify SD_BUS_NAME_ALLOW_REPLACEMENT while acquiring the + name. + - - -ESRCH + + -ESRCH - It was attempted to release a name that is currently not registered on the - bus. - + It was attempted to release a name that is currently not registered on the + bus. + - - -EADDRINUSE + + -EADDRINUSE - It was attempted to release a name that is owned by a different peer on the - bus. - + It was attempted to release a name that is owned by a different peer on the + bus. + - - -EINVAL + + -EINVAL - A specified parameter is invalid. This is also generated when the requested name is a special - service name reserved by the D-Bus specification, or when the operation is requested on a connection that does - not refer to a bus. - + A specified parameter is invalid. This is also generated when the requested name is + a special service name reserved by the D-Bus specification, or when the operation is requested on a + connection that does not refer to a bus. + - - -ENOTCONN + + -ENOTCONN - The bus connection has been disconnected. - + The bus connection has been disconnected. + - - -ECHILD + + -ECHILD - The bus connection has been created in a different process than the current - one. - - + The bus connection has been created in a different process than the current + one. + + + diff --git a/man/sd_bus_set_close_on_exit.xml b/man/sd_bus_set_close_on_exit.xml index dc4f6a3e1..751fc0a72 100644 --- a/man/sd_bus_set_close_on_exit.xml +++ b/man/sd_bus_set_close_on_exit.xml @@ -1,10 +1,7 @@ - - - + + @@ -71,20 +68,20 @@ sd_bus_get_close_on_exit() 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. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - - + The bus connection has been created in a different process. + + + diff --git a/man/sd_bus_set_connected_signal.xml b/man/sd_bus_set_connected_signal.xml index 32fc630cf..edb0df205 100644 --- a/man/sd_bus_set_connected_signal.xml +++ b/man/sd_bus_set_connected_signal.xml @@ -1,10 +1,7 @@ - - - + + @@ -77,22 +74,22 @@ Return Value - On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style - error code. - + On success, these functions return 0 or a positive integer. On failure, they return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - - + The bus connection has been created in a different process. + + + diff --git a/man/sd_bus_set_description.xml b/man/sd_bus_set_description.xml index af02c20dd..cfcebdfb2 100644 --- a/man/sd_bus_set_description.xml +++ b/man/sd_bus_set_description.xml @@ -1,10 +1,7 @@ - - - + + @@ -119,55 +116,55 @@ Return Value - On success, these functions return 0 or a positive integer. On failure, - they return a negative errno-style error code. - + On success, these functions return 0 or a positive integer. On failure, they return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - An argument is invalid. - - + An argument is invalid. + + - - - -ENOPKG + + + -ENOPKG - The bus cannot be resolved. - - + The bus cannot be resolved. + + - - - -EPERM + + + -EPERM - The bus has already been started. - - + The bus has already been started. + + - - - -ECHILD + + + -ECHILD - The bus was created in a different process. - - + The bus was created in a different process. + + - - - -ENOMEM + + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_set_sender.xml b/man/sd_bus_set_sender.xml index 556e72cef..b3f67d9f9 100644 --- a/man/sd_bus_set_sender.xml +++ b/man/sd_bus_set_sender.xml @@ -1,10 +1,7 @@ - - - + + @@ -66,28 +63,29 @@ Return Value - On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style - error code. - + On success, these functions return 0 or a positive integer. On failure, they return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - + The bus connection has been created in a different process. + - - -EPERM + + -EPERM - The specified bus connection object is a not a direct but a brokered connection. - - + The specified bus connection object is a not a direct but a brokered + connection. + + + diff --git a/man/sd_bus_set_watch_bind.xml b/man/sd_bus_set_watch_bind.xml index 129b98c5f..cbdc7dd1e 100644 --- a/man/sd_bus_set_watch_bind.xml +++ b/man/sd_bus_set_watch_bind.xml @@ -1,10 +1,7 @@ - - - + + @@ -83,22 +80,22 @@ Return Value - On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style - error code. - + On success, these functions return 0 or a positive integer. On failure, they return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ECHILD + + + -ECHILD - The bus connection has been created in a different process. - - + The bus connection has been created in a different process. + + + diff --git a/man/sd_bus_slot_ref.xml b/man/sd_bus_slot_ref.xml index c5f050635..c73f3c9e2 100644 --- a/man/sd_bus_slot_ref.xml +++ b/man/sd_bus_slot_ref.xml @@ -1,5 +1,5 @@ - diff --git a/man/sd_bus_slot_set_description.xml b/man/sd_bus_slot_set_description.xml index 4bcb1e361..13dd6f881 100644 --- a/man/sd_bus_slot_set_description.xml +++ b/man/sd_bus_slot_set_description.xml @@ -1,5 +1,5 @@ - @@ -60,37 +60,37 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - An required argument is NULL. - - + An required argument is NULL. + + - - - -ENXIO + + + -ENXIO - The bus slot object has no description. - - + The bus slot object has no description. + + - - - -ENOMEM + + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_bus_slot_set_destroy_callback.xml b/man/sd_bus_slot_set_destroy_callback.xml index 80bfd865d..c6fe72e37 100644 --- a/man/sd_bus_slot_set_destroy_callback.xml +++ b/man/sd_bus_slot_set_destroy_callback.xml @@ -1,7 +1,6 @@ - - + Return Value On success, sd_bus_slot_set_destroy_callback() and - sd_bus_track_set_destroy_callback() return 0 or a positive integer. On failure, they return a - negative errno-style error code. + sd_bus_track_set_destroy_callback() return 0 or a positive integer. On failure, they + return a negative errno-style error code. sd_bus_slot_get_destroy_callback() and - sd_bus_track_get_destroy_callback() return positive if the destroy callback function is set, 0 - if not. On failure, they return a negative errno-style error code. - + sd_bus_track_get_destroy_callback() return positive if the destroy callback function + is set, 0 if not. On failure, they return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The slot or track parameter is - NULL. - - + The slot or track parameter is + NULL. + + + diff --git a/man/sd_bus_slot_set_floating.xml b/man/sd_bus_slot_set_floating.xml index 2b8c90099..f63907aa4 100644 --- a/man/sd_bus_slot_set_floating.xml +++ b/man/sd_bus_slot_set_floating.xml @@ -1,7 +1,6 @@ - - + @@ -71,35 +70,35 @@ Return Value - On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style - error code. - + On success, these functions return 0 or a positive integer. On failure, they return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The slot parameter is NULL. - + The slot parameter is NULL. + - - -ECHILD + + -ECHILD - The bus connection has been created in a different process. - + The bus connection has been created in a different process. + - - -ESTALE + + -ESTALE - The bus object the specified bus slot object is associated with has already been freed, and - hence no change in the floating state can be made anymore. - - + The bus object the specified bus slot object is associated with has already been + freed, and hence no change in the floating state can be made anymore. + + + diff --git a/man/sd_bus_slot_set_userdata.xml b/man/sd_bus_slot_set_userdata.xml index dad708b6a..f0456b441 100644 --- a/man/sd_bus_slot_set_userdata.xml +++ b/man/sd_bus_slot_set_userdata.xml @@ -1,5 +1,5 @@ - diff --git a/man/sd_bus_track_add_name.xml b/man/sd_bus_track_add_name.xml index ef304fc06..29501adf5 100644 --- a/man/sd_bus_track_add_name.xml +++ b/man/sd_bus_track_add_name.xml @@ -1,10 +1,7 @@ - - - + + @@ -184,36 +181,36 @@ sd_bus_track_first() and sd_bus_track_next() return the first/next name contained in the bus peer tracking object, and NULL if the end of the enumeration is reached and on error. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EUNATCH + + -EUNATCH - sd_bus_track_remove_name() or - sd_bus_track_remove_sender() have been invoked for a name not previously added to the bus - peer object. - + sd_bus_track_remove_name() or + sd_bus_track_remove_sender() have been invoked for a name not previously added + to the bus peer object. + - - -EINVAL + + -EINVAL - Specified parameter is invalid. - + Specified parameter is invalid. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - + Memory allocation failed. + - + + diff --git a/man/sd_bus_track_new.xml b/man/sd_bus_track_new.xml index 711eca3fc..9b79eb4c2 100644 --- a/man/sd_bus_track_new.xml +++ b/man/sd_bus_track_new.xml @@ -1,10 +1,7 @@ - - - + + @@ -185,37 +182,38 @@ reference. When not needed anymore, this reference should be destroyed with sd_bus_track_unref(). - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EBUSY + + -EBUSY - Bus peers have already been added to the bus peer tracking object and - sd_bus_track_set_recursive() was called to change tracking mode. - + Bus peers have already been added to the bus peer tracking object and + sd_bus_track_set_recursive() was called to change tracking mode. + + - - -EINVAL + + -EINVAL - Specified parameter is invalid - (NULL in case of output - parameters). - + Specified parameter is invalid + (NULL in case of output + parameters). + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - + Memory allocation failed. + - + + diff --git a/man/sd_bus_wait.xml b/man/sd_bus_wait.xml index e866eeb20..7b97dc02f 100644 --- a/man/sd_bus_wait.xml +++ b/man/sd_bus_wait.xml @@ -1,6 +1,6 @@ - + + + @@ -140,61 +137,61 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate an object. - + Not enough memory to allocate an object. + - - -EINVAL + + -EINVAL - An invalid argument has been passed. This includes - specifying an empty mask in options or a mask - which contains values different than a combination of - WEXITED, WSTOPPED, and - WCONTINUED. - + An invalid argument has been passed. This includes + specifying an empty mask in options or a mask + which contains values different than a combination of + WEXITED, WSTOPPED, and + WCONTINUED. + - + - - -EBUSY + + -EBUSY - A handler is already installed for this - child process. + A handler is already installed for this + child process. - + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - - -EDOM + + -EDOM - The passed event source is not a child process event source. - + The passed event source is not a child process event source. + - + + diff --git a/man/sd_event_add_defer.xml b/man/sd_event_add_defer.xml index 82ddce665..636c61e99 100644 --- a/man/sd_event_add_defer.xml +++ b/man/sd_event_add_defer.xml @@ -1,10 +1,7 @@ - - - + + @@ -132,39 +129,39 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate an object. - + Not enough memory to allocate an object. + - - -EINVAL + + -EINVAL - An invalid argument has been passed. - + An invalid argument has been passed. + - - -ESTALE + + -ESTALE - The event loop is already terminated. - + The event loop is already terminated. + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. - + The event loop has been created in a different process. + - + + diff --git a/man/sd_event_add_inotify.xml b/man/sd_event_add_inotify.xml index 65765ea3c..3c9f2fbe3 100644 --- a/man/sd_event_add_inotify.xml +++ b/man/sd_event_add_inotify.xml @@ -1,7 +1,6 @@ - - + @@ -111,48 +110,48 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate an object. - + Not enough memory to allocate an object. + - - -EINVAL + + -EINVAL - An invalid argument has been passed. This includes specifying a mask with - IN_MASK_ADD set. - + An invalid argument has been passed. This includes specifying a mask with + IN_MASK_ADD set. + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - - -EDOM + + -EDOM - The passed event source is not an inotify process event source. - + The passed event source is not an inotify process event source. + - + + diff --git a/man/sd_event_add_io.xml b/man/sd_event_add_io.xml index 6b3da2b9b..398b938e3 100644 --- a/man/sd_event_add_io.xml +++ b/man/sd_event_add_io.xml @@ -1,10 +1,7 @@ - - - + + @@ -164,6 +161,12 @@ "floating", and will be destroyed implicitly when the event loop itself is destroyed. + Note that this call does not take possession of the file descriptor passed in, ownership (and thus + the duty to close it when it is no longer needed) remains with the caller. However, with the + sd_event_source_set_io_fd_own() call (see below) the event source may optionally + take ownership of the file descriptor after the event source has been created. In that case the file + descriptor is closed automatically as soon as the event source is released. + It is recommended to use sd_event_add_io() only in conjunction with file descriptors that have O_NONBLOCK set, to @@ -231,46 +234,46 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned values may indicate the following problems: + Returned values may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate an object. - + Not enough memory to allocate an object. + - - -EINVAL + + -EINVAL - An invalid argument has been passed. + An invalid argument has been passed. - + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. - + The event loop has been created in a different process. + - - -EDOM + + -EDOM - The passed event source is not an I/O event source. - - + The passed event source is not an I/O event source. + + + diff --git a/man/sd_event_add_signal.xml b/man/sd_event_add_signal.xml index 8f03d059f..9bf777c10 100644 --- a/man/sd_event_add_signal.xml +++ b/man/sd_event_add_signal.xml @@ -1,10 +1,7 @@ - - - + + @@ -124,52 +121,52 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate an object. - + Not enough memory to allocate an object. + - - -EINVAL + + -EINVAL - An invalid argument has been passed. - + An invalid argument has been passed. + - - -EBUSY + + -EBUSY - A handler is already installed for this - signal or the signal was not blocked previously. - + A handler is already installed for this + signal or the signal was not blocked previously. + - - -ESTALE + + -ESTALE - The event loop is already terminated. - + The event loop is already terminated. + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. - + The event loop has been created in a different process. + - - -EDOM + + -EDOM - The passed event source is not a signal event source. - + The passed event source is not a signal event source. + - + + diff --git a/man/sd_event_add_time.xml b/man/sd_event_add_time.xml index e6dbd15a6..731636451 100644 --- a/man/sd_event_add_time.xml +++ b/man/sd_event_add_time.xml @@ -1,10 +1,7 @@ - - - + + @@ -212,54 +209,54 @@ On success, these functions return 0 or a positive integer. On failure, they return a negative errno-style error code. - - - Errors + + Errors - Returned values may indicate the following problems: + Returned values may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate an object. - + Not enough memory to allocate an object. + - - -EINVAL + + -EINVAL - An invalid argument has been passed. + An invalid argument has been passed. - + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - - -EOPNOTSUPP + + -EOPNOTSUPP - The selected clock is not supported by the event loop implementation. + The selected clock is not supported by the event loop implementation. - + - - -EDOM + + -EDOM - The passed event source is not a timer event source. - - + The passed event source is not a timer event source. + + + diff --git a/man/sd_event_exit.xml b/man/sd_event_exit.xml index 2af275b2b..a02897af0 100644 --- a/man/sd_event_exit.xml +++ b/man/sd_event_exit.xml @@ -1,10 +1,7 @@ - - - + + @@ -82,45 +79,44 @@ Return Value - On success, sd_event_exit() and - sd_event_get_exit_code() return 0 or a positive - integer. On failure, they return a negative errno-style error - code. - + On success, sd_event_exit() and sd_event_get_exit_code() + return 0 or a positive integer. On failure, they return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - The event loop object or error code pointer are invalid. + The event loop object or error code pointer are invalid. - + - - -ECHILD + + -ECHILD - The event loop was created in a different process. - + The event loop was created in a different process. + - - -ESTALE + + -ESTALE - The event loop has exited already and all exit handlers are already processed. - + The event loop has exited already and all exit handlers are already processed. + + - - -ENODATA + + -ENODATA - The event loop has not been requested to exit yet. - + The event loop has not been requested to exit yet. + - + + diff --git a/man/sd_event_get_fd.xml b/man/sd_event_get_fd.xml index 5f66fbcff..3e7818213 100644 --- a/man/sd_event_get_fd.xml +++ b/man/sd_event_get_fd.xml @@ -1,10 +1,7 @@ - - - + + @@ -61,32 +58,30 @@ Return Value - On success, sd_event_get_fd() returns a - non-negative file descriptor. On failure, it returns a negative - errno-style error code. - + On success, sd_event_get_fd() returns a non-negative file descriptor. On + failure, it returns a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - event is not a valid - pointer to an sd_event structure. - - + event is not a valid pointer to an + sd_event structure. + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - - + + + diff --git a/man/sd_event_new.xml b/man/sd_event_new.xml index ddb8dac5a..3db39c9d0 100644 --- a/man/sd_event_new.xml +++ b/man/sd_event_new.xml @@ -1,10 +1,7 @@ - - - + + @@ -161,44 +158,40 @@ Return Value - On success, sd_event_new(), - sd_event_default() and - sd_event_get_tid() return 0 or a positive - integer. On failure, they return a negative errno-style error - code. sd_event_ref() always returns a pointer - to the event loop object passed - in. sd_event_unref() always returns + On success, sd_event_new(), sd_event_default() and + sd_event_get_tid() return 0 or a positive integer. On failure, they return a + negative errno-style error code. sd_event_ref() always returns a pointer to the + event loop object passed in. sd_event_unref() always returns NULL. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -ENOMEM + + + -ENOMEM - Not enough memory to allocate the object. - + Not enough memory to allocate the object. + - - -EMFILE + + -EMFILE - The maximum number of event loops has been allocated. + The maximum number of event loops has been allocated. - + - - -ENXIO + + -ENXIO - sd_event_get_tid() was - invoked on an event loop object that was not allocated with - sd_event_default(). - + sd_event_get_tid() was invoked on an event loop object that + was not allocated with sd_event_default(). + - + + diff --git a/man/sd_event_now.xml b/man/sd_event_now.xml index 1c8afb270..2d2b08501 100644 --- a/man/sd_event_now.xml +++ b/man/sd_event_now.xml @@ -1,10 +1,7 @@ - - - + + @@ -67,42 +64,37 @@ Return Value - If the first event loop iteration has not run yet - sd_event_now() writes current time to - usec and returns a positive return value. - Otherwise, it will write the requested timestamp to usec - and return 0. On failure, the call returns a negative errno-style - error code. - + If the first event loop iteration has not run yet sd_event_now() writes + current time to usec and returns a positive return value. Otherwise, it will + write the requested timestamp to usec and return 0. On failure, the call returns a + negative errno-style error code. - - Errors + + Errors - Returned values may indicate the following problems: + Returned values may indicate the following problems: - - - -EINVAL + + + -EINVAL - An invalid parameter was - passed. + An invalid parameter was passed. - + - - -EOPNOTSUPP + + -EOPNOTSUPP - Unsupported clock type. - - + Unsupported clock type. + - - -ECHILD + + -ECHILD - The event loop object was created in a - different process. - - + The event loop object was created in a different process. + + + diff --git a/man/sd_event_run.xml b/man/sd_event_run.xml index 1a4467357..5c33d61fd 100644 --- a/man/sd_event_run.xml +++ b/man/sd_event_run.xml @@ -1,10 +1,7 @@ - - - + + @@ -99,47 +96,47 @@ dispatched. sd_event_loop() returns the exit code specified when invoking sd_event_exit(). - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The event parameter is - invalid or NULL. - + The event parameter is invalid or + NULL. + - - -EBUSY + + -EBUSY - The event loop object is not in the right - state (see - sd_event_prepare3 - for an explanation of possible states). - + The event loop object is not in the right + state (see + sd_event_prepare3 + for an explanation of possible states). + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - + - Other errors are possible, too. + Other errors are possible, too. + diff --git a/man/sd_event_set_watchdog.xml b/man/sd_event_set_watchdog.xml index 7d7155db3..faaaad39d 100644 --- a/man/sd_event_set_watchdog.xml +++ b/man/sd_event_set_watchdog.xml @@ -1,10 +1,7 @@ - - - + + @@ -96,36 +93,32 @@ Return Value On success, sd_event_set_watchdog() and - sd_event_get_watchdog() return a non-zero - positive integer if the service manager requested watchdog support - and watchdog support was successfully enabled. They return zero if - the service manager did not request watchdog support, or if - watchdog support was explicitly disabled with a false - b parameter. On failure, they return a - negative errno-style error - code. - + sd_event_get_watchdog() return a non-zero positive integer if the service manager + requested watchdog support and watchdog support was successfully enabled. They return zero if the service + manager did not request watchdog support, or if watchdog support was explicitly disabled with a false + b parameter. On failure, they return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. - + The event loop has been created in a different process. + - - -EINVAL + + -EINVAL - The passed event loop object was invalid. - + The passed event loop object was invalid. + - + + diff --git a/man/sd_event_source_get_event.xml b/man/sd_event_source_get_event.xml index 36e6c1bb9..2b059a352 100644 --- a/man/sd_event_source_get_event.xml +++ b/man/sd_event_source_get_event.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/sd_event_source_get_pending.xml b/man/sd_event_source_get_pending.xml index 75f26907d..603d4ad37 100644 --- a/man/sd_event_source_get_pending.xml +++ b/man/sd_event_source_get_pending.xml @@ -1,10 +1,7 @@ - - - + + @@ -72,56 +69,52 @@ Return Value - On success, - sd_event_source_get_pending() returns an - integer greater than zero when the event source is marked pending, - and zero when the event source is not marked pending. On failure, - it returns a negative errno-style error code. - + On success, sd_event_source_get_pending() returns an integer greater than zero + when the event source is marked pending, and zero when the event source is not marked pending. On + failure, it returns a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - source is not a valid - pointer to an sd_event_source - object. - + source is not a valid pointer to an + sd_event_source object. + - - -EDOM + + -EDOM - source refers to an - event source object created with - sd_event_add_exit3. - + source refers to an event source object created with + sd_event_add_exit3. + - - -ENOMEM + + -ENOMEM - Not enough memory. - + Not enough memory. + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - + + diff --git a/man/sd_event_source_set_description.xml b/man/sd_event_source_set_description.xml index be3df3ffa..717cc5be9 100644 --- a/man/sd_event_source_set_description.xml +++ b/man/sd_event_source_set_description.xml @@ -1,10 +1,7 @@ - - - + + @@ -82,49 +79,45 @@ Return Value On success, sd_event_source_set_description() and - sd_event_source_get_description() return a - non-negative integer. On failure, they return a negative - errno-style error code. - + sd_event_source_get_description() return a non-negative integer. On failure, they + return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - source is not a valid - pointer to an sd_event_source - object or the description argument for - sd_event_source_get_description() is - NULL. - + source is not a valid pointer to an + sd_event_source object or the description argument + for sd_event_source_get_description() is NULL. + + - - -ENOMEM + + -ENOMEM - Not enough memory to copy the - name. - + Not enough memory to copy the name. + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - - -ENXIO + + -ENXIO - No name was set for the event - source. - + No name was set for the event source. + - + + diff --git a/man/sd_event_source_set_destroy_callback.xml b/man/sd_event_source_set_destroy_callback.xml index d9afb4df2..2ffca9ec3 100644 --- a/man/sd_event_source_set_destroy_callback.xml +++ b/man/sd_event_source_set_destroy_callback.xml @@ -1,7 +1,6 @@ - - + Return Value - On success, sd_event_source_set_destroy_callback() returns 0 or a positive integer. On - failure, it returns a negative errno-style error code. + On success, sd_event_source_set_destroy_callback() returns 0 or a positive + integer. On failure, it returns a negative errno-style error code. - sd_event_source_get_destroy_callback() returns positive if the destroy callback function - is set, 0 if not. On failure, returns a negative errno-style error code. - + sd_event_source_get_destroy_callback() returns positive if the destroy + callback function is set, 0 if not. On failure, returns a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The source parameter is NULL. - - + The source parameter is NULL. + + + + diff --git a/man/sd_event_source_set_enabled.xml b/man/sd_event_source_set_enabled.xml index 73f01848d..6a7a39b42 100644 --- a/man/sd_event_source_set_enabled.xml +++ b/man/sd_event_source_set_enabled.xml @@ -1,10 +1,7 @@ - - - + + @@ -103,39 +100,38 @@ Return Value On success, sd_event_source_set_enabled() returns a non-negative - integer. sd_event_source_get_enabled() returns zero if the source is - disabled (SD_EVENT_OFF) and a positive integer otherwise. On failure, they - return a negative errno-style error code. - + integer. sd_event_source_get_enabled() returns zero if the source is disabled + (SD_EVENT_OFF) and a positive integer otherwise. On failure, they return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - source is not a valid - pointer to an sd_event_source - object. - + source is not a valid pointer to an + sd_event_source object. + - - -ENOMEM + + -ENOMEM - Not enough memory. - + Not enough memory. + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - + + diff --git a/man/sd_event_source_set_prepare.xml b/man/sd_event_source_set_prepare.xml index ae71f74f5..63c33ca19 100644 --- a/man/sd_event_source_set_prepare.xml +++ b/man/sd_event_source_set_prepare.xml @@ -1,10 +1,7 @@ - - - + + @@ -76,55 +73,51 @@ Return Value - On success, - sd_event_source_set_prepare() returns a - non-negative integer. On failure, it returns a negative - errno-style error code. - + On success, sd_event_source_set_prepare() returns a non-negative integer. On + failure, it returns a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - source is not a valid - pointer to an sd_event_source - object. - + source is not a valid pointer to an + sd_event_source object. + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - - - -ENOMEM + + + -ENOMEM - Not enough memory. - + Not enough memory. + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - - -EDOM + + -EDOM - The specified event source has been created - with - sd_event_add_exit3. + The specified event source has been created with + sd_event_add_exit3. - + - + + diff --git a/man/sd_event_source_set_priority.xml b/man/sd_event_source_set_priority.xml index dd1662864..d1cb416dc 100644 --- a/man/sd_event_source_set_priority.xml +++ b/man/sd_event_source_set_priority.xml @@ -1,10 +1,7 @@ - - - + + @@ -109,48 +106,45 @@ Return Value - On success, - sd_event_source_set_priority() and - sd_event_source_get_priority() return a - non-negative integer. On failure, they return a negative - errno-style error code. - + On success, sd_event_source_set_priority() and + sd_event_source_get_priority() return a non-negative integer. On failure, they + return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - source is not a valid - pointer to an sd_event_source - object. - + source is not a valid pointer to an + sd_event_source object. + - - -ENOMEM + + -ENOMEM - Not enough memory. - + Not enough memory. + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - + + diff --git a/man/sd_event_source_set_userdata.xml b/man/sd_event_source_set_userdata.xml index e01328256..fe315845f 100644 --- a/man/sd_event_source_set_userdata.xml +++ b/man/sd_event_source_set_userdata.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/sd_event_source_unref.xml b/man/sd_event_source_unref.xml index b11df3f7b..01e3008ee 100644 --- a/man/sd_event_source_unref.xml +++ b/man/sd_event_source_unref.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/sd_event_wait.xml b/man/sd_event_wait.xml index f01d18e94..210a0c948 100644 --- a/man/sd_event_wait.xml +++ b/man/sd_event_wait.xml @@ -1,10 +1,7 @@ - - - + + @@ -257,56 +254,51 @@ Return Value - On success, these functions return 0 or a positive integer. - On failure, they return a negative errno-style error code. In case - of sd_event_prepare() and - sd_event_wait(), a positive, non-zero return - code indicates that events are ready to be processed and zero - indicates that no events are ready. In case of - sd_event_dispatch(), a positive, non-zero - return code indicates that the event loop returned to its initial - state and zero indicates the event loop has - exited. sd_event_get_state() returns a - positive or zero state on success. - + On success, these functions return 0 or a positive integer. On failure, they return a negative + errno-style error code. In case of sd_event_prepare() and + sd_event_wait(), a positive, non-zero return code indicates that events are ready to + be processed and zero indicates that no events are ready. In case of + sd_event_dispatch(), a positive, non-zero return code indicates that the event loop + returned to its initial state and zero indicates the event loop has + exited. sd_event_get_state() returns a positive or zero state on success. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - - - -EINVAL + + + -EINVAL - The event parameter is - invalid or NULL. - + The event parameter is invalid or NULL. + + - - -EBUSY + + -EBUSY - The event loop object is not in the right - state. - + The event loop object is not in the right state. + - - -ESTALE + + -ESTALE - The event loop is already terminated. + The event loop is already terminated. - + - - -ECHILD + + -ECHILD - The event loop has been created in a different process. + The event loop has been created in a different process. - + - + - Other errors are possible, too. + Other errors are possible, too. + diff --git a/man/sd_get_seats.xml b/man/sd_get_seats.xml index 925516dc1..ed4b77b3e 100644 --- a/man/sd_get_seats.xml +++ b/man/sd_get_seats.xml @@ -1,10 +1,7 @@ - - - + @@ -86,27 +83,24 @@ Return Value - On success, sd_get_seats(), - sd_get_sessions(), - sd_get_uids() and - sd_get_machine_names() return the number of - entries in the arrays. On failure, these calls return a negative - errno-style error code. - + On success, sd_get_seats(), sd_get_sessions(), + sd_get_uids() and sd_get_machine_names() return the number of + entries in the arrays. On failure, these calls return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_id128_get_machine.xml b/man/sd_id128_get_machine.xml index 0bfe1b500..e665f7343 100644 --- a/man/sd_id128_get_machine.xml +++ b/man/sd_id128_get_machine.xml @@ -1,10 +1,7 @@ - - - + @@ -118,12 +115,53 @@ Return Value Those calls return 0 on success (in which case ret is filled in), - or a negative errno-style error code. In particular, - sd_id128_get_machine(), - sd_id128_get_machine_app_specific(), and - sd_id128_get_boot_app_specific() return -ENOENT if - /etc/machine-id is missing, and -ENOMEDIUM if - /etc/machine-id is empty or all zeros. + or a negative errno-style error code. + + + Errors + Returned errors may indicate the following problems: + + + + -ENOENT + + Returned by sd_id128_get_machine(), + sd_id128_get_machine_app_specific(), and + sd_id128_get_boot_app_specific() when /etc/machine-id is + missing. + + + + -ENOMEDIUM + + Returned by sd_id128_get_machine(), + sd_id128_get_machine_app_specific(), and + sd_id128_get_boot_app_specific() when /etc/machine-id is + empty or all zeros. + + + + -ENXIO + + Returned by sd_id128_get_invocation() if no invocation ID is + set. + + + + -EIO + + Returned by any of the functions described here when the configured value has + invalid format. + + + + -EPERM + + Requested information could not be retrieved because of insufficient permissions. + + + + diff --git a/man/sd_id128_randomize.xml b/man/sd_id128_randomize.xml index 4f5b160bd..9cf55ddab 100644 --- a/man/sd_id128_randomize.xml +++ b/man/sd_id128_randomize.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_id128_to_string.xml b/man/sd_id128_to_string.xml index ca071854e..4f585e35a 100644 --- a/man/sd_id128_to_string.xml +++ b/man/sd_id128_to_string.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_is_fifo.xml b/man/sd_is_fifo.xml index 05709d8ba..26a0db120 100644 --- a/man/sd_is_fifo.xml +++ b/man/sd_is_fifo.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_add_match.xml b/man/sd_journal_add_match.xml index d82f71ffa..83f7fe924 100644 --- a/man/sd_journal_add_match.xml +++ b/man/sd_journal_add_match.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_enumerate_fields.xml b/man/sd_journal_enumerate_fields.xml index c5704f53a..791d1c746 100644 --- a/man/sd_journal_enumerate_fields.xml +++ b/man/sd_journal_enumerate_fields.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_catalog.xml b/man/sd_journal_get_catalog.xml index 80edc08c8..8c7058a13 100644 --- a/man/sd_journal_get_catalog.xml +++ b/man/sd_journal_get_catalog.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_cursor.xml b/man/sd_journal_get_cursor.xml index d5e465b81..851bfde81 100644 --- a/man/sd_journal_get_cursor.xml +++ b/man/sd_journal_get_cursor.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_cutoff_realtime_usec.xml b/man/sd_journal_get_cutoff_realtime_usec.xml index b2a0634f7..a76855fea 100644 --- a/man/sd_journal_get_cutoff_realtime_usec.xml +++ b/man/sd_journal_get_cutoff_realtime_usec.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_data.xml b/man/sd_journal_get_data.xml index 464fd16ac..0a0030e30 100644 --- a/man/sd_journal_get_data.xml +++ b/man/sd_journal_get_data.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_fd.xml b/man/sd_journal_get_fd.xml index 2186b685b..fc55bbd5f 100644 --- a/man/sd_journal_get_fd.xml +++ b/man/sd_journal_get_fd.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_realtime_usec.xml b/man/sd_journal_get_realtime_usec.xml index e0f5c4d2e..f8150ded1 100644 --- a/man/sd_journal_get_realtime_usec.xml +++ b/man/sd_journal_get_realtime_usec.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_get_usage.xml b/man/sd_journal_get_usage.xml index 39f53dd5e..636837156 100644 --- a/man/sd_journal_get_usage.xml +++ b/man/sd_journal_get_usage.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_has_runtime_files.xml b/man/sd_journal_has_runtime_files.xml index 44fdc8d18..4b0075cbe 100644 --- a/man/sd_journal_has_runtime_files.xml +++ b/man/sd_journal_has_runtime_files.xml @@ -1,5 +1,5 @@ - - - - + diff --git a/man/sd_journal_open.xml b/man/sd_journal_open.xml index cf787b7ea..723621c46 100644 --- a/man/sd_journal_open.xml +++ b/man/sd_journal_open.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_print.xml b/man/sd_journal_print.xml index e18cf88bb..ad9412ed5 100644 --- a/man/sd_journal_print.xml +++ b/man/sd_journal_print.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_query_unique.xml b/man/sd_journal_query_unique.xml index 9adafa114..1bf83968d 100644 --- a/man/sd_journal_query_unique.xml +++ b/man/sd_journal_query_unique.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_journal_seek_head.xml b/man/sd_journal_seek_head.xml index da88d241e..8e505f91e 100644 --- a/man/sd_journal_seek_head.xml +++ b/man/sd_journal_seek_head.xml @@ -1,10 +1,7 @@ - - - + @@ -66,46 +63,38 @@ Description - sd_journal_seek_head() seeks to the - beginning of the journal, i.e. the oldest available entry. + sd_journal_seek_head() seeks to the beginning of the journal, i.e. to the + position before the oldest available entry. - Similarly, sd_journal_seek_tail() may - be used to seek to the end of the journal, i.e. the most recent - available entry. + Similarly, sd_journal_seek_tail() may be used to seek to the end of the + journal, i.e. the position after the most recent available entry. - sd_journal_seek_monotonic_usec() seeks - to the entry with the specified monotonic timestamp, i.e. - CLOCK_MONOTONIC. Since monotonic time - restarts on every reboot a boot ID needs to be specified as - well. + sd_journal_seek_monotonic_usec() seeks to a position with the specified + monotonic timestamp, i.e. CLOCK_MONOTONIC. Since monotonic time restarts on every + reboot a boot ID needs to be specified as well. - sd_journal_seek_realtime_usec() seeks - to the entry with the specified realtime (wallclock) timestamp, - i.e. CLOCK_REALTIME. Note that the realtime - clock is not necessarily monotonic. If a realtime timestamp is - ambiguous, it is not defined which position is sought to. + sd_journal_seek_realtime_usec() seeks to a position with the specified + realtime (wallclock) timestamp, i.e. CLOCK_REALTIME. Note that the realtime clock is + not necessarily monotonic. If a realtime timestamp is ambiguous, it is not defined which position is + sought to. - sd_journal_seek_cursor() seeks to the - entry located at the specified cursor string. For details on - cursors, see + sd_journal_seek_cursor() seeks to the position at the specified cursor + string. For details on cursors, see sd_journal_get_cursor3. - If no entry matching the specified cursor is found the call will - seek to the next closest entry (in terms of time) instead. To - verify whether the newly selected entry actually matches the - cursor, use + If no entry matching the specified cursor is found the call will seek to the next closest entry (in terms + of time) instead. To verify whether the newly selected entry actually matches the cursor, use sd_journal_test_cursor3. - Note that these calls do not actually make any entry the new - current entry, this needs to be done in a separate step with a - subsequent + Note that these calls do not actually make any entry the new current entry, this needs to be done + in a separate step with a subsequent sd_journal_next3 - invocation (or a similar call). Only then, entry data may be - retrieved via - sd_journal_get_data3. - If no entry exists that matches exactly the specified seek - address, the next closest is sought to. If - sd_journal_next3 - is used, the closest following entry will be sought to, if + invocation (or a similar call). Only then, entry data may be retrieved via + sd_journal_get_data3 + or an entry cursor be retrieved via + sd_journal_get_cursor3. + If no entry exists that matches exactly the specified seek address, the next closest is sought to. If + sd_journal_next3 is + used, the closest following entry will be sought to, if sd_journal_previous3 is used the closest preceding entry is sought to. diff --git a/man/sd_journal_stream_fd.xml b/man/sd_journal_stream_fd.xml index a3ff90865..bdb27cc8a 100644 --- a/man/sd_journal_stream_fd.xml +++ b/man/sd_journal_stream_fd.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_listen_fds.xml b/man/sd_listen_fds.xml index 3c3d2ff26..9a66ee33b 100644 --- a/man/sd_listen_fds.xml +++ b/man/sd_listen_fds.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_login_monitor_new.xml b/man/sd_login_monitor_new.xml index d914e51d0..be1c843ee 100644 --- a/man/sd_login_monitor_new.xml +++ b/man/sd_login_monitor_new.xml @@ -1,10 +1,7 @@ - - - + @@ -209,29 +206,28 @@ else { sd_login_monitor_unref() always returns NULL. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -EINVAL + + -EINVAL - An input parameter was invalid (out of range, - or NULL, where that is not accepted). The specified category to - watch is not known. - + An input parameter was invalid (out of range, or NULL, where + that is not accepted). The specified category to watch is not known. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_machine_get_class.xml b/man/sd_machine_get_class.xml index bacbe1455..db6cf0dbc 100644 --- a/man/sd_machine_get_class.xml +++ b/man/sd_machine_get_class.xml @@ -1,10 +1,7 @@ - - - + @@ -69,38 +66,37 @@ Return Value - On success, these calls return 0 or a positive integer. On - failure, these calls return a negative errno-style error - code. - + On success, these calls return 0 or a positive integer. On failure, these calls return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ENXIO + + -ENXIO - The specified machine does not exist or is currently not running. - - + The specified machine does not exist or is currently not running. + + - - -EINVAL + + -EINVAL - An input parameter was invalid (out of range, - or NULL, where that is not accepted). - + An input parameter was invalid (out of range, or NULL, where + that is not accepted). + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_notify.xml b/man/sd_notify.xml index 79ae84bb9..0084bf388 100644 --- a/man/sd_notify.xml +++ b/man/sd_notify.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/sd_pid_get_owner_uid.xml b/man/sd_pid_get_owner_uid.xml index f4619b610..e9d7a8eb6 100644 --- a/man/sd_pid_get_owner_uid.xml +++ b/man/sd_pid_get_owner_uid.xml @@ -1,10 +1,7 @@ - - - + @@ -243,54 +240,50 @@ Return Value - On success, these calls return 0 or a positive integer. On - failure, these calls return a negative errno-style error - code. - + On success, these calls return 0 or a positive integer. On failure, these calls return a negative + errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ESRCH + + -ESRCH - The specified PID does not refer to a running - process. - - + The specified PID does not refer to a running process. + + - - -EBADF + + -EBADF - The specified socket file descriptor was - invalid. - + The specified socket file descriptor was invalid. + - - -ENODATA + + -ENODATA - The given field is not specified for the described - process or peer. - - + The given field is not specified for the described process or peer. + + - - -EINVAL + + -EINVAL - An input parameter was invalid (out of range, - or NULL, where that is not accepted). - + An input parameter was invalid (out of range, or NULL, where + that is not accepted). + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_seat_get_active.xml b/man/sd_seat_get_active.xml index 2fc5fdeea..2dba6803f 100644 --- a/man/sd_seat_get_active.xml +++ b/man/sd_seat_get_active.xml @@ -1,10 +1,7 @@ - - - + @@ -115,52 +112,47 @@ Return Value - On success, sd_seat_get_active() - returns 0 or a positive integer. On success, - sd_seat_get_sessions() returns the number of - entries in the session identifier array. If the test succeeds, - sd_seat_can_multi_session, - sd_seat_can_tty and - sd_seat_can_graphical return a positive - integer, if it fails 0. On failure, these calls return a negative - errno-style error code. - + On success, sd_seat_get_active() returns 0 or a positive integer. On success, + sd_seat_get_sessions() returns the number of entries in the session identifier + array. If the test succeeds, sd_seat_can_multi_session, + sd_seat_can_tty and sd_seat_can_graphical return a positive + integer, if it fails 0. On failure, these calls return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ENODATA + + -ENODATA - The given field is not specified for the described - seat. - - + The given field is not specified for the described seat. + + - - -ENXIO + + -ENXIO - The specified seat is unknown. - - + The specified seat is unknown. + + - - -EINVAL + + -EINVAL - An input parameter was invalid (out of range, - or NULL, where that is not accepted). - + An input parameter was invalid (out of range, or NULL, where + that is not accepted). + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_session_is_active.xml b/man/sd_session_is_active.xml index a0f7a9312..d4a12f533 100644 --- a/man/sd_session_is_active.xml +++ b/man/sd_session_is_active.xml @@ -1,10 +1,7 @@ - - - + @@ -265,43 +262,42 @@ sd_session_get_tty() return 0 or a positive integer. On failure, these calls return a negative errno-style error code. - - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ENXIO + + -ENXIO - The specified session does not exist. - - + The specified session does not exist. + + - - -ENODATA + + -ENODATA - The given field is not specified for the described - session. - - + The given field is not specified for the described session. + + - - -EINVAL + + -EINVAL - An input parameter was invalid (out of range, - or NULL, where that is not accepted). - + An input parameter was invalid (out of range, or NULL, where + that is not accepted). + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_uid_get_state.xml b/man/sd_uid_get_state.xml index 02670e1bc..ed38559bb 100644 --- a/man/sd_uid_get_state.xml +++ b/man/sd_uid_get_state.xml @@ -1,10 +1,7 @@ - - - + @@ -134,55 +131,48 @@ Return Value - On success, sd_uid_get_state() returns - 0 or a positive integer. If the test succeeds, - sd_uid_is_on_seat() returns a positive - integer; if it fails, 0. - sd_uid_get_sessions() and - sd_uid_get_seats() return the number of - entries in the returned arrays. - sd_uid_get_display() returns a non-negative - code on success. On failure, these calls return a negative - errno-style error code. - + On success, sd_uid_get_state() returns 0 or a positive integer. If the test + succeeds, sd_uid_is_on_seat() returns a positive integer; if it fails, 0. + sd_uid_get_sessions() and sd_uid_get_seats() return the number + of entries in the returned arrays. sd_uid_get_display() returns a non-negative code + on success. On failure, these calls return a negative errno-style error code. - - Errors + + Errors - Returned errors may indicate the following problems: + Returned errors may indicate the following problems: - + - - -ENODATA + + -ENODATA - The given field is not specified for the described - user. - - + The given field is not specified for the described user. + + - - -ENXIO + + -ENXIO - The specified seat is unknown. - - + The specified seat is unknown. + + - - -EINVAL + + -EINVAL - An input parameter was invalid (out of range, - or NULL, where that is not accepted). This is also returned if - the passed user ID is 0xFFFF or 0xFFFFFFFF, which are - undefined on Linux. - + An input parameter was invalid (out of range, or NULL, where that is not + accepted). This is also returned if the passed user ID is 0xFFFF or + 0xFFFFFFFF, which are undefined on Linux. + - - -ENOMEM + + -ENOMEM - Memory allocation failed. - - + Memory allocation failed. + + + diff --git a/man/sd_watchdog_enabled.xml b/man/sd_watchdog_enabled.xml index ad2cdd878..0aa5609a3 100644 --- a/man/sd_watchdog_enabled.xml +++ b/man/sd_watchdog_enabled.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/shutdown.xml b/man/shutdown.xml index 282a3bae2..bc2fa6074 100644 --- a/man/shutdown.xml +++ b/man/shutdown.xml @@ -1,10 +1,7 @@ - - - + @@ -122,7 +119,7 @@ Cancel a pending shutdown. This may be used - cancel the effect of an invocation of + to cancel the effect of an invocation of shutdown with a time argument that is not +0 or now. diff --git a/man/standard-conf.xml b/man/standard-conf.xml index f5c961a0c..1db859ac2 100644 --- a/man/standard-conf.xml +++ b/man/standard-conf.xml @@ -11,18 +11,16 @@ Configuration Directories and Precedence - Configuration files are read from directories in - /etc/, /run/, and - /usr/lib/, in order of precedence. - Each configuration file in these configuration directories shall be named in - the style of filename.conf. - Files in /etc/ override files with the same name in - /run/ and /usr/lib/. Files in - /run/ override files with the same name in - /usr/lib/. + Configuration files are read from directories in /etc/, /run/, + /usr/local/lib/, and /usr/lib/, in order of precedence. Each + configuration file in these configuration directories shall be named in the style of + filename.conf. Files in /etc/ override files + with the same name in /run/, /usr/local/lib/, and + /usr/lib/. Files in /run/ override files with the same name under + /usr/. - Packages should install their configuration files in - /usr/lib/. Files in /etc/ are + Packages should install their configuration files in /usr/lib/ (distribution packages) + or /usr/local/lib/ (local installs). Files in /etc/ 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 @@ -52,7 +50,8 @@ When packages need to customize the configuration, they can install configuration snippets in - /usr/lib/systemd/*.conf.d/. Files in + /usr/lib/systemd/*.conf.d/ or + /usr/local/lib/systemd/*.conf.d/. Files in /etc/ are reserved for the local administrator, who may use this logic to override the configuration files installed by vendor packages. The main diff --git a/man/standard-options.xml b/man/standard-options.xml index 51a4faf5d..6a61d9ec0 100644 --- a/man/standard-options.xml +++ b/man/standard-options.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/sysctl.d.xml b/man/sysctl.d.xml index 7c8fde0db..0a8eeb62a 100644 --- a/man/sysctl.d.xml +++ b/man/sysctl.d.xml @@ -1,8 +1,7 @@ - - + + diff --git a/man/system-only.xml b/man/system-only.xml new file mode 100644 index 000000000..94aa08bd6 --- /dev/null +++ b/man/system-only.xml @@ -0,0 +1,16 @@ + + + + + + + +This option is only available for system services and is not supported for services +running in per-user instances of the service manager. + +These options are only available for system services and are not supported for services +running in per-user instances of the service manager. + + diff --git a/man/systemctl.xml b/man/systemctl.xml index 08aacd8f4..d991e979f 100644 --- a/man/systemctl.xml +++ b/man/systemctl.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + @@ -302,6 +299,20 @@ + + + + + + When enqueuing a unit job (for example as effect of a systemctl start + invocation or similar), show brief information about all jobs enqueued, covering both the requested + job and any added because of unit dependencies. Note that the output will only include jobs + immediately part of the transaction requested. It is possible that service start-up program code + run as effect of the enqueued jobs might request further jobs to be pulled in. This means that + completion of the listed jobs might ultimately entail more jobs than the listed ones. + + + @@ -617,11 +628,30 @@ - When used with the reboot command, - indicate to the system's firmware to boot into setup - mode. Note that this is currently only supported on some EFI - systems and only if the system was booted in EFI - mode. + When used with the reboot command, indicate to the system's firmware to reboot into + the firmware setup interface. Note that this functionality is not available on all systems. + + + + + + + + When used with the reboot command, indicate to the system's boot loader to show the + boot loader menu on the following boot. Takes a time value as parameter — indicating the menu time-out. Pass + zero in order to disable the menu time-out. Note that not all boot loaders support this + functionality. + + + + + + + + When used with the reboot command, indicate to the system's boot loader to boot into + a specific boot loader entry on the following boot. Takes a boot loader entry identifier as argument, or + help in order to list available entries. Note that not all boot loaders support this + functionality. diff --git a/man/systemd-analyze.xml b/man/systemd-analyze.xml index 7becf0133..f559b858f 100644 --- a/man/systemd-analyze.xml +++ b/man/systemd-analyze.xml @@ -1,10 +1,7 @@ - - - + @@ -41,35 +38,7 @@ critical-chain UNIT - - systemd-analyze - OPTIONS - plot - > file.svg - - - systemd-analyze - OPTIONS - dot - PATTERN - > file.dot - - - systemd-analyze - OPTIONS - dump - - - systemd-analyze - OPTIONS - cat-config - NAME|PATH - - - systemd-analyze - OPTIONS - unit-paths - + systemd-analyze OPTIONS @@ -85,14 +54,40 @@ systemd-analyze OPTIONS - syscall-filter - SET + service-watchdogs + BOOL + + + + systemd-analyze + OPTIONS + dump + + + + systemd-analyze + OPTIONS + plot + >file.svg systemd-analyze OPTIONS - verify - FILES + dot + PATTERN + >file.dot + + + + systemd-analyze + OPTIONS + unit-paths + + + systemd-analyze + OPTIONS + syscall-filter + SET systemd-analyze @@ -103,14 +98,20 @@ systemd-analyze OPTIONS - service-watchdogs - BOOL + timespan + SPAN systemd-analyze OPTIONS - timespan - SPAN + cat-config + NAME|PATH + + + systemd-analyze + OPTIONS + verify + FILE systemd-analyze @@ -129,73 +130,299 @@ verify the correctness of unit files. It is also used to access special functions useful for advanced system manager debugging. - systemd-analyze time prints the time - spent in the kernel before userspace has been reached, the time - spent in the initial RAM disk (initrd) before normal system - userspace has been reached, and the time normal system userspace - took to initialize. Note that these measurements simply measure - the time passed up to the point where all system services have - been spawned, but not necessarily until they fully finished - initialization or the disk is idle. + If no command is passed, systemd-analyze + time is implied. - systemd-analyze blame prints a list of - all running units, ordered by the time they took to initialize. - This information may be used to optimize boot-up times. Note that - the output might be misleading as the initialization of one - service might be slow simply because it waits for the - initialization of another service to complete. - Also note: systemd-analyze blame doesn't display - results for services with Type=simple, - because systemd considers such services to be started immediately, - hence no measurement of the initialization delays can be done. + + <command>systemd-analyze time</command> - systemd-analyze critical-chain - [UNIT…] prints a tree of - the time-critical chain of units (for each of the specified - UNITs or for the default target - otherwise). The time after the unit is active or started is - printed after the "@" character. The time the unit takes to start - is printed after the "+" character. Note that the output might be - misleading as the initialization of one service might depend on - socket activation and because of the parallel execution of - units. + This command prints the time spent in the kernel before userspace has been reached, the time + spent in the initial RAM disk (initrd) before normal system userspace has been reached, and the time + normal system userspace took to initialize. Note that these measurements simply measure the time passed + up to the point where all system services have been spawned, but not necessarily until they fully + finished initialization or the disk is idle. - systemd-analyze plot prints an SVG - graphic detailing which system services have been started at what - time, highlighting the time they spent on initialization. + + <command>Show how long the boot took</command> - systemd-analyze dot generates textual - dependency graph description in dot format for further processing - with the GraphViz - dot1 - tool. Use a command line like systemd-analyze dot | dot - -Tsvg > systemd.svg to generate a graphical dependency - tree. Unless or - is passed, the generated graph will - show both ordering and requirement dependencies. Optional pattern - globbing style specifications (e.g. *.target) - may be given at the end. A unit dependency is included in the - graph if any of these patterns match either the origin or - destination node. + # in a container +$ systemd-analyze time +Startup finished in 296ms (userspace) +multi-user.target reached after 275ms in userspace - systemd-analyze dump outputs a (usually - very long) human-readable serialization of the complete server - state. Its format is subject to change without notice and should - not be parsed by applications. +# on a real machine +$ systemd-analyze time +Startup finished in 2.584s (kernel) + 19.176s (initrd) + 47.847s (userspace) = 1min 9.608s +multi-user.target reached after 47.820s in userspace + + + - systemd-analyze cat-config is similar - to systemctl cat, but operates on config files. - It will copy the contents of a config file and any drop-ins to standard - output, using the usual systemd set of directories and rules for - precedence. Each argument must be either an absolute path including - the prefix (such as /etc/systemd/logind.conf or - /usr/lib/systemd/logind.conf), or a name - relative to the prefix (such as systemd/logind.conf). - + + <command>systemd-analyze blame</command> - - Showing logind configuration - $ systemd-analyze cat-config systemd/logind.conf + This command prints a list of all running units, ordered by the time they took to initialize. + This information may be used to optimize boot-up times. Note that the output might be misleading as the + initialization of one service might be slow simply because it waits for the initialization of another + service to complete. Also note: systemd-analyze blame doesn't display results for + services with Type=simple, because systemd considers such services to be started + immediately, hence no measurement of the initialization delays can be done. + + + <command>Show which units took the most time during boot</command> + + $ systemd-analyze blame + 32.875s pmlogger.service + 20.905s systemd-networkd-wait-online.service + 13.299s dev-vda1.device + ... + 23ms sysroot.mount + 11ms initrd-udevadm-cleanup-db.service + 3ms sys-kernel-config.mount + + + + + + <command>systemd-analyze critical-chain <optional><replaceable>UNIT</replaceable>...</optional></command> + + This command prints a tree of the time-critical chain of units (for each of the specified + UNITs or for the default target otherwise). The time after the unit is + active or started is printed after the "@" character. The time the unit takes to start is printed after + the "+" character. Note that the output might be misleading as the initialization of services might + depend on socket activation and because of the parallel execution of units. + + + <command>systemd-analyze time</command> + + $ systemd-analyze critical-chain +multi-user.target @47.820s +└─pmie.service @35.968s +548ms + └─pmcd.service @33.715s +2.247s + └─network-online.target @33.712s + └─systemd-networkd-wait-online.service @12.804s +20.905s + └─systemd-networkd.service @11.109s +1.690s + └─systemd-udevd.service @9.201s +1.904s + └─systemd-tmpfiles-setup-dev.service @7.306s +1.776s + └─kmod-static-nodes.service @6.976s +177ms + └─systemd-journald.socket + └─system.slice + └─-.slice + + + + + + <command>systemd-analyze log-level [<replaceable>LEVEL</replaceable>]</command> + + systemd-analyze log-level prints the current log level of the + systemd daemon. If an optional argument LEVEL is + provided, then the command changes the current log level of the systemd daemon to + LEVEL (accepts the same values as described in + systemd1). + + + + <command>systemd-analyze log-target [<replaceable>TARGET</replaceable>]</command> + + systemd-analyze log-target prints the current log target of the + systemd daemon. If an optional argument TARGET is + provided, then the command changes the current log target of the systemd daemon to + TARGET (accepts the same values as , described + in systemd1). + + + + <command>systemd-analyze service-watchdogs [yes|no]</command> + + systemd-analyze service-watchdogs prints the current state of service runtime + watchdogs of the systemd daemon. If an optional boolean argument is provided, then + globally enables or disables the service runtime watchdogs () and + emergency actions (e.g. or ); see + systemd.service5. + The hardware watchdog is not affected by this setting. + + + + <command>systemd-analyze dump</command> + + This command outputs a (usually very long) human-readable serialization of the complete server + state. Its format is subject to change without notice and should not be parsed by applications. + + + Show the internal state of user manager + + $ systemd-analyze --user dump +Timestamp userspace: Thu 2019-03-14 23:28:07 CET +Timestamp finish: Thu 2019-03-14 23:28:07 CET +Timestamp generators-start: Thu 2019-03-14 23:28:07 CET +Timestamp generators-finish: Thu 2019-03-14 23:28:07 CET +Timestamp units-load-start: Thu 2019-03-14 23:28:07 CET +Timestamp units-load-finish: Thu 2019-03-14 23:28:07 CET +-> Unit proc-timer_list.mount: + Description: /proc/timer_list + ... +-> Unit default.target: + Description: Main user target +... + + + + + + <command>systemd-analyze plot</command> + + This command prints an SVG graphic detailing which system services have been started at what + time, highlighting the time they spent on initialization. + + + <command>Plot a bootchart</command> + + $ systemd-analyze plot >bootup.svg +$ eog bootup.svg& + + + + + + <command>systemd-analyze dot [<replaceable>pattern</replaceable>...]</command> + + This command generates textual dependency graph description in dot format for further processing + with the GraphViz + dot1 + tool. Use a command line like systemd-analyze dot | dot -Tsvg >systemd.svg to + generate a graphical dependency tree. Unless or is + passed, the generated graph will show both ordering and requirement dependencies. Optional pattern + globbing style specifications (e.g. *.target) may be given at the end. A unit + dependency is included in the graph if any of these patterns match either the origin or destination + node. + + + Plot all dependencies of any unit whose name starts with <literal>avahi-daemon</literal> + + + $ systemd-analyze dot 'avahi-daemon.*' | dot -Tsvg >avahi.svg +$ eog avahi.svg + + + + Plot the dependencies between all known target units + + $ systemd-analyze dot --to-pattern='*.target' --from-pattern='*.target' \ + | dot -Tsvg >targets.svg +$ eog targets.svg + + + + + <command>systemd-analyze unit-paths</command> + + This command outputs a list of all directories from which unit files, .d + overrides, and .wants, .requires symlinks may be + loaded. Combine with to retrieve the list for the user manager instance, and + for the global configuration of user manager instances. + + + <command>Show all paths for generated units</command> + + $ systemd-analyze unit-paths | grep '^/run' +/run/systemd/system.control +/run/systemd/transient +/run/systemd/generator.early +/run/systemd/system +/run/systemd/system.attached +/run/systemd/generator +/run/systemd/generator.late + + + + Note that this verb prints the list that is compiled into systemd-analyze + itself, and does not comunicate with the running manager. Use + systemctl [--user] [--global] show -p UnitPath --value + to retrieve the actual list that the manager uses, with any empty directories omitted. + + + + <command>systemd-analyze syscall-filter <optional><replaceable>SET</replaceable>...</optional></command> + + This command will list system calls contained in the specified system call set + SET, or all known sets if no sets are specified. Argument + SET must include the @ prefix. + + + + <command>systemd-analyze calendar <replaceable>EXPRESSION</replaceable>...</command> + + This command will parse and normalize repetitive calendar time events, and will calculate when + they elapse next. This takes the same input as the OnCalendar= setting in + systemd.timer5, + following the syntax described in + systemd.time7. By + default, only the next time the calendar expression will elapse is shown; use + to show the specified number of next times the expression + elapses. + + + Show leap days in the near future + + $ systemd-analyze calendar --iterations=5 '*-2-29 0:0:0' + Original form: *-2-29 0:0:0 +Normalized form: *-02-29 00:00:00 + Next elapse: Sat 2020-02-29 00:00:00 UTC + From now: 11 months 15 days left + Iter. #2: Thu 2024-02-29 00:00:00 UTC + From now: 4 years 11 months left + Iter. #3: Tue 2028-02-29 00:00:00 UTC + From now: 8 years 11 months left + Iter. #4: Sun 2032-02-29 00:00:00 UTC + From now: 12 years 11 months left + Iter. #5: Fri 2036-02-29 00:00:00 UTC + From now: 16 years 11 months left + + + + + + <command>systemd-analyze timespan <replaceable>EXPRESSION</replaceable>...</command> + + This command parses a time span and outputs the normalized form and the equivalent value in + microseconds. The time span should adhere to the same syntax documented in + systemd.time7. + Values without associated magnitudes are parsed as seconds. + + + Show parsing of timespans + + $ systemd-analyze timespan 1s 300s '1year 0.000001s' +Original: 1s + μs: 1000000 + Human: 1s + +Original: 300s + μs: 300000000 + Human: 5min + +Original: 1year 0.000001s + μs: 31557600000001 + Human: 1y 1us + + + + + + <command>systemd-analyze cat-config</command> + <replaceable>NAME</replaceable>|<replaceable>PATH</replaceable>... + + This command is similar to systemctl cat, but operates on config files. It + will copy the contents of a config file and any drop-ins to standard output, using the usual systemd + set of directories and rules for precedence. Each argument must be either an absolute path including + the prefix (such as /etc/systemd/logind.conf or + /usr/lib/systemd/logind.conf), or a name relative to the prefix (such as + systemd/logind.conf). + + + Showing logind configuration + $ systemd-analyze cat-config systemd/logind.conf # /etc/systemd/logind.conf ... [Login] @@ -207,94 +434,122 @@ NAutoVTs=8 # /etc/systemd/logind.conf.d/50-override.conf ... some administrator override - - + + + - systemd-analyze unit-paths outputs a list of all - directories from which unit files, .d overrides, and - .wants, .requires symlinks may be - loaded. Combine with to retrieve the list for the user - manager instance, and for the global configuration of - user manager instances. Note that this verb prints the list that is compiled into - systemd-analyze itself, and does not comunicate with the - running manager. Use - systemctl [--user] [--global] show -p UnitPath --value - to retrieve the actual list that the manager uses, with any empty directories - omitted. + + <command>systemd-analyze verify <replaceable>FILE</replaceable>...</command> - systemd-analyze log-level - prints the current log level of the systemd daemon. - If an optional argument LEVEL is provided, then the command changes the current log - level of the systemd daemon to LEVEL (accepts the same values as - described in - systemd1). + This command will load unit files and print warnings if any errors are detected. Files specified + on the command line will be loaded, but also any other units referenced by them. The full unit search + path is formed by combining the directories for all command line arguments, and the usual unit load + paths (variable $SYSTEMD_UNIT_PATH is supported, and may be used to replace or + augment the compiled in set of unit load paths; see + systemd.unit5). All + units files present in the directories containing the command line arguments will be used in preference + to the other paths. - systemd-analyze log-target - prints the current log target of the systemd daemon. - If an optional argument TARGET is provided, then the command changes the current log - target of the systemd daemon to TARGET (accepts the same values as - , described in - systemd1). + The following errors are currently detected: + + unknown sections and directives, - systemd-analyze syscall-filter SET - will list system calls contained in the specified system call set SET, - or all known sets if no sets are specified. Argument SET must include - the @ prefix. + missing dependencies which are required to start the given unit, - systemd-analyze verify will load unit files and print - warnings if any errors are detected. Files specified on the command line will be - loaded, but also any other units referenced by them. The full unit search path is - formed by combining the directories for all command line arguments, and the usual unit - load paths (variable $SYSTEMD_UNIT_PATH is supported, and may be - used to replace or augment the compiled in set of unit load paths; see - systemd.unit5). - All units files present in the directories containing the command line arguments will - be used in preference to the other paths. + man pages listed in Documentation= which are not found in the + system, - systemd-analyze calendar will parse and normalize repetitive calendar time events, and - will calculate when they will elapse next. This takes the same input as the OnCalendar= setting - in systemd.timer5, following the - syntax described in - systemd.time7. + commands listed in ExecStart= and similar which are not found in + the system or not executable. + - systemd-analyze service-watchdogs - prints the current state of service runtime watchdogs of the systemd daemon. - If an optional boolean argument is provided, then globally enables or disables the service - runtime watchdogs () and emergency actions (e.g. - or ); see - systemd.service5. - The hardware watchdog is not affected by this setting. + + Misspelt directives - systemd-analyze timespan 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 systemd.time7. - Values without associated magnitudes are parsed as seconds. + $ cat ./user.slice +[Unit] +WhatIsThis=11 +Documentation=man:nosuchfile(1) +Requires=different.service - systemd-analyze security 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. +[Service] +Description=x - If no command is passed, systemd-analyze - time is implied. +$ systemd-analyze verify ./user.slice +[./user.slice:9] Unknown lvalue 'WhatIsThis' in section 'Unit' +[./user.slice:13] Unknown section 'Service'. Ignoring. +Error: org.freedesktop.systemd1.LoadFailed: + Unit different.service failed to load: + No such file or directory. +Failed to create user.slice/start: Invalid argument +user.slice: man nosuchfile(1) command failed with code 16 + + + + Missing service units + + $ tail ./a.socket ./b.socket +==> ./a.socket <== +[Socket] +ListenStream=100 + +==> ./b.socket <== +[Socket] +ListenStream=100 +Accept=yes + +$ systemd-analyze verify ./a.socket ./b.socket +Service a.service not loaded, a.socket cannot be started. +Service b@0.service not loaded, b.socket cannot be started. + + + + + + <command>systemd-analyze security <optional><replaceable>UNIT</replaceable>...</optional></command> + + This 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. + + + Analyze <filename noindex="true">systemd-logind.service</filename> + + $ systemd-analyze security --no-pager systemd-logind.service + NAME DESCRIPTION EXPOSURE +✗ PrivateNetwork= Service has access to the host's network 0.5 +✗ User=/DynamicUser= Service runs as root user 0.4 +✗ DeviceAllow= Service has no device ACL 0.2 +✓ IPAddressDeny= Service blocks all IP address ranges +... +→ Overall exposure level for systemd-logind.service: 4.1 OK 🙂 + + + @@ -401,6 +656,13 @@ NAutoVTs=8 the specified root path PATH. + + + + When used with the calendar command, show the specified number of + iterations the specified calendar expression will elapse next. Defaults to 1. + + @@ -418,88 +680,6 @@ NAutoVTs=8 otherwise. - - Examples for <command>dot</command> - - - Plots all dependencies of any unit whose name starts with - <literal>avahi-daemon</literal> - - $ systemd-analyze dot 'avahi-daemon.*' | dot -Tsvg > avahi.svg -$ eog avahi.svg - - - - Plots the dependencies between all known target units - - $ systemd-analyze dot --to-pattern='*.target' --from-pattern='*.target' | dot -Tsvg > targets.svg -$ eog targets.svg - - - - - Examples for <command>verify</command> - - The following errors are currently detected: - - unknown sections and directives, - - - missing dependencies which are required to start - the given unit, - - man pages listed in - Documentation= which are not found in the - system, - - commands listed in ExecStart= - and similar which are not found in the system or not - executable. - - - - Misspelt directives - - $ cat ./user.slice -[Unit] -WhatIsThis=11 -Documentation=man:nosuchfile(1) -Requires=different.service - -[Service] -Description=x - -$ systemd-analyze verify ./user.slice -[./user.slice:9] Unknown lvalue 'WhatIsThis' in section 'Unit' -[./user.slice:13] Unknown section 'Service'. Ignoring. -Error: org.freedesktop.systemd1.LoadFailed: - Unit different.service failed to load: - No such file or directory. -Failed to create user.slice/start: Invalid argument -user.slice: man nosuchfile(1) command failed with code 16 - - - - - Missing service units - - $ tail ./a.socket ./b.socket -==> ./a.socket <== -[Socket] -ListenStream=100 - -==> ./b.socket <== -[Socket] -ListenStream=100 -Accept=yes - -$ systemd-analyze verify ./a.socket ./b.socket -Service a.service not loaded, a.socket cannot be started. -Service b@0.service not loaded, b.socket cannot be started. - - - - diff --git a/man/systemd-ask-password-console.service.xml b/man/systemd-ask-password-console.service.xml index 04f959606..8c0e8a88f 100644 --- a/man/systemd-ask-password-console.service.xml +++ b/man/systemd-ask-password-console.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-ask-password.xml b/man/systemd-ask-password.xml index 2fe3e88d7..9bb045b94 100644 --- a/man/systemd-ask-password.xml +++ b/man/systemd-ask-password.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-backlight@.service.xml b/man/systemd-backlight@.service.xml index a7865b20c..6e836d692 100644 --- a/man/systemd-backlight@.service.xml +++ b/man/systemd-backlight@.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-binfmt.service.xml b/man/systemd-binfmt.service.xml index c197b6284..27e34195a 100644 --- a/man/systemd-binfmt.service.xml +++ b/man/systemd-binfmt.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-bless-boot-generator.xml b/man/systemd-bless-boot-generator.xml index 980941469..0c5144f6b 100644 --- a/man/systemd-bless-boot-generator.xml +++ b/man/systemd-bless-boot-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-bless-boot.service.xml b/man/systemd-bless-boot.service.xml index fb362cef2..cd5e9ac01 100644 --- a/man/systemd-bless-boot.service.xml +++ b/man/systemd-bless-boot.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-boot-check-no-failures.service.xml b/man/systemd-boot-check-no-failures.service.xml index 55c2adffd..d6d587f9c 100644 --- a/man/systemd-boot-check-no-failures.service.xml +++ b/man/systemd-boot-check-no-failures.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-boot.xml b/man/systemd-boot.xml index 4c914e615..3b1319687 100644 --- a/man/systemd-boot.xml +++ b/man/systemd-boot.xml @@ -1,7 +1,6 @@ - - Description - systemd-boot (short: sd-boot) is a simple UEFI boot manager. It - provides a graphical menu to select the entry to boot and an editor for the kernel command line. systemd-boot - supports systems with UEFI firmware only. + systemd-boot (short: sd-boot) is a simple UEFI boot + manager. It provides a graphical menu to select the entry to boot and an editor for the kernel command + line. systemd-boot supports systems with UEFI firmware only. systemd-boot loads boot entry information from the EFI system partition (ESP), usually mounted at - /boot, /efi, or /boot/efi during OS - runtime. Configuration file fragments, kernels, initrds and other EFI images to boot generally need to reside on - the ESP. Linux kernels must be built with to be able to be directly executed as an - EFI image. During boot systemd-boot automatically assembles a list of boot entries from the following - sources: + /efi/, /boot/, or /boot/efi/ during OS + runtime, as well as from the Extended Boot Loader partition if it exists (usually mounted to + /boot/). Configuration file fragments, kernels, initrds and other EFI images to boot + generally need to reside on the ESP or the Extended Boot Loader partition. Linux kernels must be built + with to be able to be directly executed as an EFI image. During boot + systemd-boot automatically assembles a list of boot entries from the following sources: Boot entries defined with Boot Loader - Specification description files located in /loader/entries/ on the ESP. These - usually describe Linux kernel images with associated initrd images, but alternatively may also describe - arbitrary other EFI executables. + url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification description files + located in /loader/entries/ on the ESP and the Extended Boot Loader + Partition. These usually describe Linux kernel images with associated initrd images, but alternatively + may also describe arbitrary other EFI executables. Unified kernel images following the Boot Loader - Specification, as executable EFI binaries in /EFI/Linux/ on the ESP. + url="https://systemd.io/BOOT_LOADER_SPECIFICATION">Boot Loader Specification, as executable EFI + binaries in /EFI/Linux/ on the ESP and the Extended Boot Loader Partition. The Microsoft Windows EFI boot manager, if installed @@ -57,10 +57,12 @@ A reboot into the UEFI firmware setup option, if supported by the firmware - kernel-install8 may be - used to copy kernel images onto the ESP and to generate description files compliant with the Boot Loader - Specification. bootctl1 may be - used from a running system to locate the ESP, list available entries, and install systemd-boot itself. + kernel-install8 + may be used to copy kernel images onto the ESP or the Extended Boot Loader Partition and to generate + description files compliant with the Boot Loader + Specification. bootctl1 + may be used from a running system to locate the ESP and the Extended Boot Loader Partition, list + available entries, and install systemd-boot itself. systemd-boot will provide information about the time spent in UEFI firmware using the Boot Loader Interface. This information can be displayed @@ -224,16 +226,19 @@ Files - The files systemd-boot reads generally reside on the UEFI ESP which is usually mounted to - /boot/, /efi/ or /boot/efi during OS - runtime. systemd-boot reads runtime configuration such as the boot timeout and default entry from - /loader/loader.conf on the ESP (in combination with data read from EFI variables). See - loader.conf5. Boot entry - description files following the Boot Loader - Specification are read from /loader/entries/ on the ESP. Unified kernel boot entries - following the Boot - Loader Specification are read from /EFI/Linux/ on the ESP. + The files systemd-boot processes generally reside on the UEFI ESP which is + usually mounted to /efi/, /boot/ or + /boot/efi/ during OS runtime. It also processes files on the Extended Boot Loader + partition which is typically mounted to /boot/, if it + exists. systemd-boot reads runtime configuration such as the boot timeout and default + entry from /loader/loader.conf on the ESP (in combination with data read from EFI + variables). See + loader.conf5. Boot + entry description files following the Boot + Loader Specification are read from /loader/entries/ on the ESP and the + Extended Boot Loader partition. Unified kernel boot entries following the Boot Loader Specification are read from + /EFI/Linux/ on the ESP and the Extended Boot Loader partition. diff --git a/man/systemd-cat.xml b/man/systemd-cat.xml index 446fa4ba3..f3d6f3785 100644 --- a/man/systemd-cat.xml +++ b/man/systemd-cat.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-cgls.xml b/man/systemd-cgls.xml index 0d7f2b648..6181cdf83 100644 --- a/man/systemd-cgls.xml +++ b/man/systemd-cgls.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-cgtop.xml b/man/systemd-cgtop.xml index 32ed66b3f..a850a57c9 100644 --- a/man/systemd-cgtop.xml +++ b/man/systemd-cgtop.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-coredump.xml b/man/systemd-coredump.xml index b25f0c4f1..1ab7e598a 100644 --- a/man/systemd-coredump.xml +++ b/man/systemd-coredump.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/systemd-cryptsetup-generator.xml b/man/systemd-cryptsetup-generator.xml index e30d69bfe..e3c17d64d 100644 --- a/man/systemd-cryptsetup-generator.xml +++ b/man/systemd-cryptsetup-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-cryptsetup@.service.xml b/man/systemd-cryptsetup@.service.xml index 439e8e9cd..0324a6744 100644 --- a/man/systemd-cryptsetup@.service.xml +++ b/man/systemd-cryptsetup@.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-debug-generator.xml b/man/systemd-debug-generator.xml index fa88e8ac0..1f9a79db8 100644 --- a/man/systemd-debug-generator.xml +++ b/man/systemd-debug-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-delta.xml b/man/systemd-delta.xml index f86f2052f..02d7b8851 100644 --- a/man/systemd-delta.xml +++ b/man/systemd-delta.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-detect-virt.xml b/man/systemd-detect-virt.xml index c4763fd56..28d997cfa 100644 --- a/man/systemd-detect-virt.xml +++ b/man/systemd-detect-virt.xml @@ -1,10 +1,7 @@ - - - + @@ -126,7 +123,12 @@ - Container + acrn + ACRN hypervisor + + + + Container openvz OpenVZ/Virtuozzo @@ -155,6 +157,11 @@ rkt rkt app container runtime + + + wsl + Windows Subsystem for Linux + @@ -164,6 +171,12 @@ machine and container virtualization are used in conjunction, only the latter will be identified (unless is passed). + Windows Subsystem for Linux is not a Linux container, + but an environment for running Linux userspace applications on + top of the Windows kernel using a Linux-compatible interface. + WSL is categorized as a container for practical purposes. + Multiple WSL environments share the same kernel and services + should generally behave like when being run in a container. @@ -184,7 +197,7 @@ - Only detects hardware virtualization). + Only detects hardware virtualization. diff --git a/man/systemd-environment-d-generator.xml b/man/systemd-environment-d-generator.xml index 44880e76e..4c88bd5e6 100644 --- a/man/systemd-environment-d-generator.xml +++ b/man/systemd-environment-d-generator.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + @@ -37,7 +34,7 @@ systemd-environment-d-generator is a systemd.environment-generator7 that reads environment configuration specified by - environment.d7 + environment.d5 configuration files and passes it to the systemd1 user manager instance. @@ -49,7 +46,7 @@ systemd1, systemctl1, systemd.environment-generator7, - systemd.generator7 + systemd.generator5 diff --git a/man/systemd-escape.xml b/man/systemd-escape.xml index f61c07ae9..86ef175ab 100644 --- a/man/systemd-escape.xml +++ b/man/systemd-escape.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-firstboot.xml b/man/systemd-firstboot.xml index a78e2d506..560649f22 100644 --- a/man/systemd-firstboot.xml +++ b/man/systemd-firstboot.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-fsck@.service.xml b/man/systemd-fsck@.service.xml index e1996e402..121b0cdf7 100644 --- a/man/systemd-fsck@.service.xml +++ b/man/systemd-fsck@.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-fstab-generator.xml b/man/systemd-fstab-generator.xml index ab706420c..82321c44b 100644 --- a/man/systemd-fstab-generator.xml +++ b/man/systemd-fstab-generator.xml @@ -1,9 +1,8 @@ - - + + @@ -175,19 +174,25 @@ lost at shutdown, as /etc and /var will be served from the (initially unpopulated) volatile memory file system. - If set to the generator will leave the root - directory mount point unaltered, however will mount a tmpfs file system to - /var. In this mode the normal system configuration (i.e. the contents of - /etc) is in effect (and may be modified during system runtime), however the system state - (i.e. the contents of /var) is reset at boot and lost at shutdown. + If set to the generator will leave the root directory mount point unaltered, + however will mount a tmpfs file system to /var. In this mode the normal + system configuration (i.e. the contents of /etc) is in effect (and may be modified during + system runtime), however the system state (i.e. the contents of /var) is reset at boot and + lost at shutdown. + + If this setting is set to overlay the root file system is set up as + overlayfs mount combining the read-only root directory with a writable + tmpfs, so that no modifications are made to disk, but the file system may be modified + nonetheless with all changes being lost at reboot. Note that in none of these modes the root directory, /etc, /var or any other resources stored in the root file system are physically removed. It's thus safe to boot a system that is normally operated in non-volatile mode temporarily into volatile mode, without losing data. - Note that enabling this setting will only work correctly on operating systems that can boot up with only - /usr mounted, and are able to automatically populate /etc, and also - /var in case of systemd.volatile=yes. + Note that with the exception of overlay mode, enabling this setting will only work + correctly on operating systems that can boot up with only /usr mounted, and are able to + automatically populate /etc, and also /var in case of + systemd.volatile=yes. diff --git a/man/systemd-getty-generator.xml b/man/systemd-getty-generator.xml index b5a300624..4cc664dbd 100644 --- a/man/systemd-getty-generator.xml +++ b/man/systemd-getty-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-gpt-auto-generator.xml b/man/systemd-gpt-auto-generator.xml index d98ef2003..0d6d4e307 100644 --- a/man/systemd-gpt-auto-generator.xml +++ b/man/systemd-gpt-auto-generator.xml @@ -1,9 +1,8 @@ - - + + @@ -32,22 +31,18 @@ Description - systemd-gpt-auto-generator is a unit - generator that automatically discovers root, - /home, /srv and swap - partitions and creates mount and swap units for them, based on the - partition type GUIDs of GUID partition tables (GPT), - see UEFI Specification, chapter 5. - It implements the Discoverable - Partitions Specification. Note that this generator has no - effect on non-GPT systems, or where the directories under the - mount points are already non-empty. Also, on systems where the - units are explicitly configured (for example, listed in - fstab5), - the units this generator creates are overridden, but additional - implicit dependencies might be created. + systemd-gpt-auto-generator is a unit generator that automatically discovers + root, /home/, /srv/, the EFI System Partition, the Extended + Boot Loader Partition and swap partitions and creates mount and swap units for them, based on the + partition type GUIDs of GUID partition tables (GPT), see UEFI Specification, chapter 5. It implements the Discoverable Partitions + Specification. Note that this generator has no effect on non-GPT systems, and on specific mount + points that are directories already containing files. Also, on systems where the units are explicitly + configured (for example, listed in fstab5), the + units this generator creates are overridden, but additional implicit dependencies might be + created. This generator will only look for root partitions on the same physical disk the EFI System Partition (ESP) is located on. @@ -70,11 +65,13 @@ + Partition Type GUID Name + Mount Point Explanation @@ -82,48 +79,63 @@ 44479540-f297-41b2-9af7-d131d5f0458a Root Partition (x86) + / On 32-bit x86 systems, the first x86 root partition on the disk the EFI ESP is located on is mounted to the root directory /. 4f68bce3-e8cd-4db1-96e7-fbcaf984b709 Root Partition (x86-64) + / On 64-bit x86 systems, the first x86-64 root partition on the disk the EFI ESP is located on is mounted to the root directory /. 69dad710-2ce4-4e3c-b16c-21a1d49abed3 Root Partition (32-bit ARM) + / On 32-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. b921b045-1df0-41c3-af44-4c6f280d3fae Root Partition (64-bit ARM) + / On 64-bit ARM systems, the first ARM root partition on the disk the EFI ESP is located on is mounted to the root directory /. 993d8d3d-f80e-4225-855a-9daf8ed7ea97 Root Partition (Itanium/IA-64) + / On Itanium systems, the first Itanium root partition on the disk the EFI ESP is located on is mounted to the root directory /. 933ac7e1-2eb4-4f13-b844-0e14e2aef915 Home Partition + /home/ The first home partition on the disk the root partition is located on is mounted to /home. 3b8f8425-20e0-4f3b-907f-1a25a76f98e8 Server Data Partition + /srv/ The first server data partition on the disk the root partition is located on is mounted to /srv. 0657fd6d-a4ab-43c4-84e5-0933c84b4f4f Swap + n/a All swap partitions located on the disk the root partition is located on are enabled. c12a7328-f81f-11d2-ba4b-00a0c93ec93b EFI System Partition (ESP) + /efi/ or /boot/ The first ESP located on the disk the root partition is located on is mounted to /boot or /efi, see below. + + bc13c2ff-59e6-4262-a352-b275fd6f7172 + Extended Boot Loader Partition + /boot/ + The first Extended Boot Loader Partition is mounted to /boot, see below. + @@ -149,44 +161,49 @@ GPT_FLAG_READ_ONLY 0x1000000000000000 - /, /srv, /home + /, /home/, /srv/, Extended Boot Loader Partition Partition is mounted read-only GPT_FLAG_NO_AUTO 0x8000000000000000 - /, /srv, /home + /, /home/, /srv/, Extended Boot Loader Partition Partition is not mounted automatically GPT_FLAG_NO_BLOCK_IO_PROTOCOL 0x0000000000000002 - ESP + EFI System Partition (ESP) Partition is not mounted automatically - The /home and /srv - partitions may be encrypted in LUKS format. In this case, a device - mapper device is set up under the names - /dev/mapper/home and - /dev/mapper/srv. Note that this might create - conflicts if the same partition is listed in - /etc/crypttab with a different device mapper - device name. + The /home/ and /srv/ partitions may be encrypted in LUKS + format. In this case, a device mapper device is set up under the names + /dev/mapper/home and /dev/mapper/srv. Note that this might + create conflicts if the same partition is listed in /etc/crypttab with a different + device mapper device name. - Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP is mounted - to /boot, unless a mount point directory /efi exists, in which case it is - mounted there. Since this generator creates an automount unit, the mount will only be activated on-demand, when - accessed. On systems where /boot (or /efi if it exists) is an explicitly - configured mount (for example, listed in fstab5) or where the - /boot (or /efi) mount point is non-empty, no mount units are - generated. + Mount and automount units for the EFI System Partition (ESP) are generated on EFI systems. The ESP + is mounted to /boot/ (except if an Extended Boot Loader partition exists, see + below), unless a mount point directory /efi/ exists, in which case it is mounted + there. Since this generator creates an automount unit, the mount will only be activated on-demand, when + accessed. On systems where /boot/ (or /efi/ if it exists) is an + explicitly configured mount (for example, listed in fstab5) or where + the /boot/ (or /efi/) mount point is non-empty, no mount units + are generated. + + If the disk contains an Extended Boot Loader partition, as defined in the Boot Loader Specification, it is made + available at /boot (by means of an automount point, similar to the ESP, see + above). If both an EFI System Partition and an Extended Boot Loader partition exist the latter is + preferably mounted to /boot/. Make sure to create both /efi/ + and /boot/ to ensure both partitions are mounted. When using this generator in conjunction with btrfs file systems, make sure to set the correct default subvolumes on them, @@ -196,6 +213,45 @@ systemd.generator7. + + Kernel Command Line + + systemd-gpt-auto-generator understands the following kernel command line + parameters: + + + + + systemd.gpt_auto + rd.systemd.gpt_auto + + Those options take an optional boolean argument, and default to yes. + The generator is enabled by default, and a negative value may be used to disable it. + + + + + root= + + When used with the special value gpt-auto, automatic discovery of + the root parition based on the GPT partition type is enabled. Any other value disables this + generator. + + + + rw + ro + + Mount the root partition read-write or read-only initially. + + Note that unlike most kernel command line options these settings do not override configuration + in the file system, and the file system may be remounted later. See + systemd-remount-fs.service8. + + + + + See Also diff --git a/man/systemd-halt.service.xml b/man/systemd-halt.service.xml index a40384ce5..2b102cc94 100644 --- a/man/systemd-halt.service.xml +++ b/man/systemd-halt.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-hibernate-resume-generator.xml b/man/systemd-hibernate-resume-generator.xml index 8f0cc5d04..ff105d435 100644 --- a/man/systemd-hibernate-resume-generator.xml +++ b/man/systemd-hibernate-resume-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-hibernate-resume@.service.xml b/man/systemd-hibernate-resume@.service.xml index 0db49e32e..88858ef6c 100644 --- a/man/systemd-hibernate-resume@.service.xml +++ b/man/systemd-hibernate-resume@.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-hostnamed.service.xml b/man/systemd-hostnamed.service.xml index 95c8bb6d3..19bd4c06f 100644 --- a/man/systemd-hostnamed.service.xml +++ b/man/systemd-hostnamed.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-hwdb.xml b/man/systemd-hwdb.xml index 6c8487a89..15a2c0130 100644 --- a/man/systemd-hwdb.xml +++ b/man/systemd-hwdb.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-id128.xml b/man/systemd-id128.xml index 8a76cccd8..b6906b0dc 100644 --- a/man/systemd-id128.xml +++ b/man/systemd-id128.xml @@ -1,5 +1,5 @@ - diff --git a/man/systemd-importd.service.xml b/man/systemd-importd.service.xml index ec967c471..355e5b0b9 100644 --- a/man/systemd-importd.service.xml +++ b/man/systemd-importd.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-inhibit.xml b/man/systemd-inhibit.xml index 3fa5acf55..e0399ebbb 100644 --- a/man/systemd-inhibit.xml +++ b/man/systemd-inhibit.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-initctl.service.xml b/man/systemd-initctl.service.xml index f9627c929..0345936fc 100644 --- a/man/systemd-initctl.service.xml +++ b/man/systemd-initctl.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-journal-gatewayd.service.xml b/man/systemd-journal-gatewayd.service.xml index 13604a041..633b4cb55 100644 --- a/man/systemd-journal-gatewayd.service.xml +++ b/man/systemd-journal-gatewayd.service.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/systemd-journal-remote.service.xml b/man/systemd-journal-remote.service.xml index 6e6819be0..b28092d18 100644 --- a/man/systemd-journal-remote.service.xml +++ b/man/systemd-journal-remote.service.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/systemd-journal-upload.service.xml b/man/systemd-journal-upload.service.xml index cdeadddda..1b8b0be67 100644 --- a/man/systemd-journal-upload.service.xml +++ b/man/systemd-journal-upload.service.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/systemd-journald.service.xml b/man/systemd-journald.service.xml index 916799301..8bbb9931c 100644 --- a/man/systemd-journald.service.xml +++ b/man/systemd-journald.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-localed.service.xml b/man/systemd-localed.service.xml index baf20c05d..f620aade1 100644 --- a/man/systemd-localed.service.xml +++ b/man/systemd-localed.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-logind.service.xml b/man/systemd-logind.service.xml index 1c29b3377..a66c53d07 100644 --- a/man/systemd-logind.service.xml +++ b/man/systemd-logind.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-machine-id-commit.service.xml b/man/systemd-machine-id-commit.service.xml index 06e70fc29..781a9845f 100644 --- a/man/systemd-machine-id-commit.service.xml +++ b/man/systemd-machine-id-commit.service.xml @@ -1,6 +1,7 @@ - + + diff --git a/man/systemd-machined.service.xml b/man/systemd-machined.service.xml index b5f3bc286..8771434e5 100644 --- a/man/systemd-machined.service.xml +++ b/man/systemd-machined.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-makefs@.service.xml b/man/systemd-makefs@.service.xml index c8e201eb8..89662b8ae 100644 --- a/man/systemd-makefs@.service.xml +++ b/man/systemd-makefs@.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-modules-load.service.xml b/man/systemd-modules-load.service.xml index 5aa70b709..ca9edef36 100644 --- a/man/systemd-modules-load.service.xml +++ b/man/systemd-modules-load.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-mount.xml b/man/systemd-mount.xml index 610c97f94..e557ee80a 100644 --- a/man/systemd-mount.xml +++ b/man/systemd-mount.xml @@ -1,10 +1,7 @@ - - - + + @@ -58,13 +55,14 @@ the service manager job queue, so that it may pull in further dependencies (such as parent mounts, or a file system checker to execute a priori), and may make use of the auto-mounting logic. - The command takes either one or two arguments. If only one argument is specified it should refer to a block - device or regular file containing a file system (e.g. /dev/sdb1 or - /path/to/disk.img). If it is a block device, which is then probed for a label and other - metadata, and is mounted to a directory whose name is generated from the label. In this mode the block device must - exist at the time of invocation of the command, so that it may be probed. If the device is found to be a removable - block device (e.g. a USB stick) an automount point instead of a regular mount point is created (i.e. the - option is implied, see below). + The command takes either one or two arguments. If only one argument is specified it should refer to + a block device or regular file containing a file system (e.g. /dev/sdb1 or + /path/to/disk.img). The block device or image file is then probed for a file system + label and other metadata, and is mounted to a directory below /run/media/system/ + whose name is generated from the file system label. In this mode the block device or image file must + exist at the time of invocation of the command, so that it may be probed. If the device is found to be a + removable block device (e.g. a USB stick) an automount point instead of a regular mount point is created + (i.e. the option is implied, see below). If two arguments are specified the first indicates the mount source (the WHAT) and the second indicates the path to mount it on (the WHERE). In this mode no probing of the @@ -303,6 +301,15 @@ + + Example + + Use a udev rule like the following to automatically mount all USB storage plugged in: + + ACTION=="add", SUBSYSTEMS=="usb", SUBSYSTEM=="block", ENV{ID_FS_USAGE}=="filesystem", \ + RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode" + + See Also diff --git a/man/systemd-networkd-wait-online.service.xml b/man/systemd-networkd-wait-online.service.xml index 95abf5b87..7c82f68fb 100644 --- a/man/systemd-networkd-wait-online.service.xml +++ b/man/systemd-networkd-wait-online.service.xml @@ -1,12 +1,10 @@ - + - - - + systemd-networkd-wait-online.service @@ -37,8 +35,9 @@ configured. By default, it will wait for all links it is aware of and which are managed by systemd-networkd.service8 - to be fully configured or failed, and for at least one link to - gain a carrier. + to be fully configured or failed, and for at least one link to be online. Here, online means that + the link's operational state is equal or higher than degraded. The threshold + can be configured by option. @@ -48,30 +47,72 @@ - - + INTERFACE:OPERSTATE + INTERFACE:OPERSTATE - Network interface to wait for before deciding - if the system is online. This is useful when a system has - several interfaces which will be configured, but a particular - one is necessary to access some network resources. This option - may be used more than once to wait for multiple network - interfaces. When used, all other interfaces are ignored. + Network interface to wait for before deciding if the system is online. This + is useful when a system has several interfaces which will be configured, but a particular + one is necessary to access some network resources. When used, all other interfaces are ignored. + This option may be used more than once to wait for multiple network interfaces. When this + option is specified multiple times, then systemd-networkd-wait-online waits + for all specified interfaces to be online. Optinally, required minimum operational state can be + specified after a colon :. Please see + networkctl1 + for possible operational states. If the operational state is not specified here, then + the value from RequiredForOnline= in the corresponding + .network file is used if present, and degraded otherwise. + - + INTERFACE + Network interfaces to be ignored when deciding if the system is online. By default, only the loopback interface is ignored. This option may be used more than once to ignore multiple network interfaces. + - + OPERSTATE + OPERSTATE + + Takes an operational state. Please see + networkctl1 + for possible operational states. If set, the specified value overrides + RequiredForOnline= settings in .network files. + But this does not override operational states specified in option. + + + + + + + Even if several interfaces are in configuring state, + systemd-networkd-wait-online exits with success when at least one interface + becomes online. When this option is specified with , then + systemd-networkd-wait-online waits for one of the specified interfaces to be + online. This option is useful when some interfaces may not have carrier on boot. + + + + + SECS + Fail the service if the network is not online by the time the timeout elapses. A timeout of 0 disables the timeout. Defaults to 120 seconds. + + + + + + Suppress log messages. + + + + @@ -80,7 +121,8 @@ systemd1, systemd.service5, - systemd-networkd.service8 + systemd-networkd.service8, + networkctl1 diff --git a/man/systemd-networkd.service.xml b/man/systemd-networkd.service.xml index 2127bf181..b3ba621ae 100644 --- a/man/systemd-networkd.service.xml +++ b/man/systemd-networkd.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-notify.xml b/man/systemd-notify.xml index 18d87329a..89060680f 100644 --- a/man/systemd-notify.xml +++ b/man/systemd-notify.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-nspawn.xml b/man/systemd-nspawn.xml index ff6b6a9a8..5ba162b93 100644 --- a/man/systemd-nspawn.xml +++ b/man/systemd-nspawn.xml @@ -1,13 +1,10 @@ - ]> - - + @@ -136,6 +133,75 @@ The following options are understood: + + + + + + Turns off any status output by the tool + itself. When this switch is used, the only output from nspawn + will be the console output of the container OS + itself. + + + + MODE + + Controls whether + systemd-nspawn shall search for and use + additional per-container settings from + .nspawn files. Takes a boolean or the + special values or + . + + If enabled (the default), a settings file named after the + machine (as specified with the + setting, or derived from the directory or image file name) + with the suffix .nspawn is searched in + /etc/systemd/nspawn/ and + /run/systemd/nspawn/. If it is found + there, its settings are read and used. If it is not found + there, it is subsequently searched in the same directory as the + image file or in the immediate parent of the root directory of + the container. In this case, if the file is found, its settings + will be also read and used, but potentially unsafe settings + are ignored. Note that in both these cases, settings on the + command line take precedence over the corresponding settings + from loaded .nspawn files, if both are + specified. Unsafe settings are considered all settings that + elevate the container's privileges or grant access to + additional resources such as files or directories of the + host. For details about the format and contents of + .nspawn files, consult + systemd.nspawn5. + + If this option is set to , the + file is searched, read and used the same way, however, the order of + precedence is reversed: settings read from the + .nspawn file will take precedence over + the corresponding command line options, if both are + specified. + + If this option is set to , the + file is searched, read and used the same way, but regardless + of being found in /etc/systemd/nspawn/, + /run/systemd/nspawn/ or next to the image + file or container root directory, all settings will take + effect, however, command line arguments still take precedence + over corresponding settings. + + If disabled, no .nspawn file is read + and no settings except the ones on the command line are in + effect. + + + + + + Image Options + + + @@ -160,16 +226,18 @@ - Directory or btrfs subvolume to use as template for the container's root - directory. If this is specified and the container's root directory (as configured by - ) does not yet exist it is created as btrfs snapshot (if - supported) or plain directory (otherwise) and populated from this template tree. Ideally, the specified - template path refers to the root of a btrfs subvolume, in which case a simple copy-on-write - snapshot is taken, and populating the root directory is instant. If the specified template path does not refer - to the root of a btrfs subvolume (or not even to a btrfs file system at - all), the tree is copied (though possibly in a copy-on-write scheme — if the file system supports that), which - can be substantially more time-consuming. May not be specified together with or - . + Directory or btrfs subvolume to use as template for the + container's root directory. If this is specified and the container's root directory (as configured by + ) does not yet exist it is created as btrfs snapshot + (if supported) or plain directory (otherwise) and populated from this template tree. Ideally, the + specified template path refers to the root of a btrfs subvolume, in which case a + simple copy-on-write snapshot is taken, and populating the root directory is instant. If the + specified template path does not refer to the root of a btrfs subvolume (or not + even to a btrfs file system at all), the tree is copied (though possibly in a + 'reflink' copy-on-write scheme — if the file system supports that), which can be substantially more + time-consuming. Note that the snapshot taken is of the specified directory or subvolume, including + all subdirectories and subvolumes below it, but excluding any sub-mounts. May not be specified + together with or . Note that this switch leaves host name, machine ID and all other settings that could identify the instance @@ -183,9 +251,18 @@ If specified, the container is run with a temporary snapshot of its file system that is removed immediately when the container terminates. May not be specified together with . - Note that this switch leaves host name, machine ID and - all other settings that could identify the instance - unmodified. + Note that this switch leaves host name, machine ID and all other settings that could identify + the instance unmodified. Please note that — as with — taking the + temporary snapshot is more efficient on file systems that support subvolume snapshots or 'reflinks' + natively (btrfs or new xfs) than on more traditional file + systems that do not (ext4). Note that the snapshot taken is of the specified + directory or subvolume, including all subdirectories and subvolumes below it, but excluding any + sub-mounts. + + With this option no modifications of the container image are retained. Use + (described below) for other mechanisms to restrict persistency of + container images during runtime. + @@ -230,6 +307,75 @@ together with , . + + + + Takes the path to an OCI runtime bundle to invoke, as specified in the OCI Runtime Specification. In + this case no .nspawn file is loaded, and the root directory and various settings are read + from the OCI runtime JSON data (but data passed on the command line takes precedence). + + + + + + Mount the container's root file system (and any other file systems container in the container + image) read-only. This has no effect on additional mounts made with , + and similar options. This mode is implied if the container image file or directory is + marked read-only itself. It is also implied if is used. In this case the container + image on disk is strictly read-only, while changes are permitted but kept non-persistently in memory only. For + further details, see below. + + + + + MODE + + Boots the container in volatile mode. When no mode parameter is passed or when mode is + specified as , full volatile mode is enabled. This means the root directory is mounted as a + mostly unpopulated tmpfs instance, and /usr/ from the OS tree is + mounted into it in read-only mode (the system thus starts up with read-only OS image, but pristine state and + configuration, any changes are lost on shutdown). When the mode parameter is specified as + , the OS tree is mounted read-only, but /var/ is mounted as a + writable tmpfs instance into it (the system thus starts up with read-only OS resources and + configuration, but pristine state, and any changes to the latter are lost on shutdown). When the mode parameter + is specified as the read-only root file system is combined with a writable + tmpfs instance through overlayfs, so that it appears at it normally + would, but any changes are applied to the temporary file system only and lost when the container is + terminated. When the mode parameter is specified as (the default), the whole OS tree is + made available writable (unless is specified, see above). + + Note that if one of the volatile modes is chosen, its effect is limited to the root file system (or + /var/ in case of ), and any other mounts placed in the hierarchy are + unaffected — regardless if they are established automatically (e.g. the EFI system partition that might be + mounted to /efi/ or /boot/) or explicitly (e.g. through an additional + command line option such as , see below). This means, even if + is used changes to /efi/ or + /boot/ are prohibited in case such a partition exists in the container image operated on, + and even if is used the hypothetical file /etc/foobar is + potentially writable if if used to mount it from outside the read-only + container /etc directory. + + The option is closely related to this setting, and provides similar + behaviour by making a temporary, ephemeral copy of the whole OS image and executing that. For further details, + see above. + + The and options provide similar functionality, but + for specific sub-directories of the OS image only. For details, see below. + + This option provides similar functionality for containers as the systemd.volatile= + kernel command line switch provides for host systems. See + kernel-command-line7 for + details. + + Note that setting this option to or will only work correctly + with operating systems in the container that can boot up with only /usr mounted, and are + able to automatically populate /var, and also /etc in case of + --volatile=yes. The option does not require any particular + preparations in the OS, but do note that overlayfs behaviour differs from regular file + systems in a number of ways, and hence compatibility is limited. + + @@ -245,6 +391,27 @@ used, also as formatted hexadecimal characters. + + + + Pivot the specified directory to / inside the container, and either unmount the + container's old root, or pivot it to another specified directory. Takes one of: a path argument — in which case the + specified path will be pivoted to / and the old root will be unmounted; or a colon-separated pair + of new root path and pivot destination for the old root. The new root path will be pivoted to /, + and the old / will be pivoted to the other directory. Both paths must be absolute, and are resolved + in the container's file system namespace. + + This is for containers which have several bootable directories in them; for example, several + OSTree deployments. It emulates the behavior of + the boot loader and initial RAM disk which normally select which directory to mount as the root and start the + container's PID 1 in. + + + + + Execution Options + + @@ -319,19 +486,14 @@ - + + - Pivot the specified directory to / inside the container, and either unmount the - container's old root, or pivot it to another specified directory. Takes one of: a path argument — in which case the - specified path will be pivoted to / and the old root will be unmounted; or a colon-separated pair - of new root path and pivot destination for the old root. The new root path will be pivoted to /, - and the old / will be pivoted to the other directory. Both paths must be absolute, and are resolved - in the container's file system namespace. - - This is for containers which have several bootable directories in them; for example, several - OSTree deployments. It emulates the behavior of - the boot loader and initial RAM disk which normally select which directory to mount as the root and start the - container's PID 1 in. + Specifies an environment variable assignment + to pass to the init process in the container, in the format + NAME=VALUE. This may be used to override + the default variables or to set additional variables. This + parameter may be used more than once. @@ -345,6 +507,36 @@ destructive operations only. + + + + Specify the process signal to send to the container's PID 1 when nspawn itself receives + SIGTERM, in order to trigger an orderly shutdown of the container. Defaults to + SIGRTMIN+3 if is used (on systemd-compatible init systems + SIGRTMIN+3 triggers an orderly shutdown). If is not used and this + option is not specified the container's processes are terminated abruptly via SIGKILL. For + a list of valid signals, see signal7. + + + + + + Configures support for notifications from the container's init process. + takes a boolean ( and ). + With option systemd-nspawn notifies systemd + with a READY=1 message when the init process is created. + With option systemd-nspawn waits for the + READY=1 message from the init process in the container + before sending its own to systemd. For more details about notifications + see sd_notify3). + + + + + System Identity Options + + @@ -389,7 +581,12 @@ /etc/machine-id in the container is unpopulated. + + + Property Options + + @@ -410,6 +607,42 @@ + + + + Controls whether the container is registered with + systemd-machined8. Takes a + boolean argument, which defaults to yes. This option should be enabled when the container + runs a full Operating System (more specifically: a system and service manager as PID 1), and is useful to + ensure that the container is accessible via + machinectl1 and shown by + tools such as ps1. If the container + does not run a service manager, it is recommended to set this option to + no. + + + + + + Instead of creating a transient scope unit to run the container in, simply use the service or + scope unit systemd-nspawn has been invoked in. If is set + this unit is registered with + systemd-machined8. This + switch should be used if systemd-nspawn is invoked from within a service unit, and the + service unit's sole purpose is to run a single systemd-nspawn container. This option is not + available if run from a user session. + Note that passing disables the effect of and + . Use and in + combination to disable any kind of unit allocation or registration with + systemd-machined. + + + + + User Namespacing Options + + @@ -502,6 +735,13 @@ + + + + Networking Options + + + @@ -518,23 +758,6 @@ - - - - Takes the path to a file representing a kernel - network namespace that the container shall run in. The specified path - should refer to a (possibly bind-mounted) network namespace file, as - exposed by the kernel below /proc/$PID/ns/net. - This makes the container enter the given network namespace. One of the - typical use cases is to give a network namespace under - /run/netns created by ip-netns8, - for example, . - Note that this option cannot be used together with other - network-related options, such as - or . - - @@ -603,6 +826,16 @@ Note that is the default if the systemd-nspawn@.service template unit file is used. + + Note that on Linux network interface names may have a length of 15 characters at maximum, while + container names may have a length up to 64 characters. As this option derives the host-side interface + name from the container name the name is possibly truncated. Thus, care needs to be taken to ensure + that interface names remain unique in this case, or even better container names are generally not + chosen longer than 12 characters, to avoid the truncation. Alternatively, the + option may be used, which allows free configuration of the + host-side interface name independently of the container name — but might require a bit more + additional configuration in case bridging in a fashion similar to + is desired. @@ -624,11 +857,13 @@ - Adds the host side of the Ethernet link created with to the - specified Ethernet bridge interface. Expects a valid network interface name of a bridge device as - argument. Note that implies . If this option - is used, the host side of the Ethernet link will use the vb- prefix instead of - ve-. + Adds the host side of the Ethernet link created with + to the specified Ethernet bridge interface. Expects a valid network interface name of a bridge device + as argument. Note that implies . If + this option is used, the host side of the Ethernet link will use the vb- prefix + instead of ve-. Regardless of the used naming prefix the same network interface + name length limits imposed by Linux apply, along with the complications this creates (for details see + above). @@ -660,6 +895,23 @@ + + + + Takes the path to a file representing a kernel + network namespace that the container shall run in. The specified path + should refer to a (possibly bind-mounted) network namespace file, as + exposed by the kernel below /proc/$PID/ns/net. + This makes the container enter the given network namespace. One of the + typical use cases is to give a network namespace under + /run/netns created by ip-netns8, + for example, . + Note that this option cannot be used together with other + network-related options, such as + or . + + @@ -678,26 +930,12 @@ , . + - - - - - Sets the SELinux security context to be used - to label processes in the container. - - - - - - - - Sets the SELinux security context to be used - to label files in the virtual API file systems in the - container. - - + + Security Options + @@ -751,6 +989,31 @@ capabilities are passed using the --capabilities=. + + + + + Sets the SELinux security context to be used + to label processes in the container. + + + + + + + + Sets the SELinux security context to be used + to label files in the virtual API file systems in the + container. + + + + + + Resource Options + + + @@ -796,58 +1059,23 @@ - + - Specify the process signal to send to the container's PID 1 when nspawn itself receives - SIGTERM, in order to trigger an orderly shutdown of the container. Defaults to - SIGRTMIN+3 if is used (on systemd-compatible init systems - SIGRTMIN+3 triggers an orderly shutdown). If is not used and this - option is not specified the container's processes are terminated abrubtly via SIGKILL. For - a list of valid signals, see signal7. + Control the architecture ("personality") + reported by + uname2 + in the container. Currently, only x86 and + x86-64 are supported. This is useful when + running a 32-bit container on a 64-bit host. If this setting + is not used, the personality reported in the container is the + same as the one reported on the host. + - - - - Control whether the container's journal shall - be made visible to the host system. If enabled, allows viewing - the container's journal files from the host (but not vice - versa). Takes one of no, - host, try-host, - guest, try-guest, - auto. If no, the journal - is not linked. If host, the journal files - are stored on the host file system (beneath - /var/log/journal/machine-id) - and the subdirectory is bind-mounted into the container at the - same location. If guest, the journal files - are stored on the guest file system (beneath - /var/log/journal/machine-id) - and the subdirectory is symlinked into the host at the same - location. try-host and - try-guest do the same but do not fail if - the host does not have persistent journaling enabled. If - auto (the default), and the right - subdirectory of /var/log/journal exists, - it will be bind mounted into the container. If the - subdirectory does not exist, no linking is performed. - Effectively, booting a container once with - guest or host will link - the journal persistently if further on the default of - auto is used. - - Note that is the default if the - systemd-nspawn@.service template unit file is used. - - - - - - Equivalent to - . - + + Integration Options + @@ -897,12 +1125,53 @@ - + - Mount the root file system read-only for the - container. + Control whether the container's journal shall + be made visible to the host system. If enabled, allows viewing + the container's journal files from the host (but not vice + versa). Takes one of no, + host, try-host, + guest, try-guest, + auto. If no, the journal + is not linked. If host, the journal files + are stored on the host file system (beneath + /var/log/journal/machine-id) + and the subdirectory is bind-mounted into the container at the + same location. If guest, the journal files + are stored on the guest file system (beneath + /var/log/journal/machine-id) + and the subdirectory is symlinked into the host at the same + location. try-host and + try-guest do the same but do not fail if + the host does not have persistent journaling enabled. If + auto (the default), and the right + subdirectory of /var/log/journal exists, + it will be bind mounted into the container. If the + subdirectory does not exist, no linking is performed. + Effectively, booting a container once with + guest or host will link + the journal persistently if further on the default of + auto is used. + + Note that is the default if the + systemd-nspawn@.service template unit file is used. + + + + Equivalent to + . + + + + + + Mount Options + + + @@ -928,23 +1197,29 @@ make them read-only, using . + + + + Make the specified path inaccessible in the container. This over-mounts the specified path + (which must exist in the container) with a file node of the same type that is empty and has the most + restrictive access mode supported. This is an effective way to mask files, directories and other file system + objects from the container payload. This option may be used more than once in case all specified paths are + masked. + + - Mount a tmpfs file system into the container. - Takes a single absolute path argument that specifies where to - mount the tmpfs instance to (in which case the directory - access mode will be chosen as 0755, owned by root/root), or - optionally a colon-separated pair of path and mount option - string that is used for mounting (in which case the kernel - default for access mode and owner will be chosen, unless - otherwise specified). This option is particularly useful for - mounting directories such as /var as - tmpfs, to allow state-less systems, in particular when - combined with . - Backslash escapes are interpreted in the path, so - \: may be used to embed colons in the path. - + Mount a tmpfs file system into the container. Takes a single absolute path argument that + specifies where to mount the tmpfs instance to (in which case the directory access mode will be chosen as 0755, + owned by root/root), or optionally a colon-separated pair of path and mount option string that is used for + mounting (in which case the kernel default for access mode and owner will be chosen, unless otherwise + specified). Backslash escapes are interpreted in the path, so \: may be used to embed colons + in the path. + + Note that this option cannot be used to replace the root file system of the container with a temporary + file system. However, the option described below provides similar + functionality, with a focus on implementing stateless operating system images. @@ -1002,177 +1277,63 @@ be on the same file system as the top-most directory tree). Also note that the lowerdir= mount option receives the paths to stack in the opposite order of - this switch. + this switch. + + Note that this option cannot be used to replace the root file system of the container with an overlay + file system. However, the option described above provides similar functionality, + with a focus on implementing stateless operating system images. + + + + + Input/Output Options + + + + MODE + + Configures how to set up standard input, output and error output for the container payload, as + well as the /dev/console device for the container. Takes one of + , , or . If + a pseudo-TTY is allocated and made available as /dev/console + in the container. It is then bi-directionally connected to the standard input and output passed to + systemd-nspawn. is similar but only the output of the container + is propagated and no input from the caller is read. In mode a pseudo TTY is allocated, + but it is not connected anywhere. Finally, in mode no pseudo TTY is allocated, but the + passed standard input, output and error output file descriptors are passed on — as they are — to the container + payload. In this mode /dev/console will not exist in the container. Note that in this mode + the container payload generally cannot be a full init system as init systems tend to require + /dev/console to be available. On the other hand, in this mode container invocations can be + used within shell pipelines. This is because intermediary pseudo TTYs do not permit independent bidirectional + propagation of the end-of-file (EOF) condition, which is necessary for shell pipelines to work + correctly. + + Note that the mode should be used carefully, as passing arbitrary file descriptors + to less trusted container payloads might open up unwanted interfaces for access by the container payload. For + example, if a passed file descriptor refers to a TTY of some form, APIs such as TIOCSTI + may be used to synthesize input that might be used for escaping the container. Hence mode + should only be used if the payload is sufficiently trusted or when the standard input/output/error output file + descriptors are known safe, for example pipes. Defaults to if + systemd-nspawn is invoked from a terminal, and + otherwise. - - + + - Specifies an environment variable assignment - to pass to the init process in the container, in the format - NAME=VALUE. This may be used to override - the default variables or to set additional variables. This - parameter may be used more than once. - - - - - - Controls whether the container is registered with - systemd-machined8. Takes a - boolean argument, which defaults to yes. This option should be enabled when the container - runs a full Operating System (more specifically: a system and service manager as PID 1), and is useful to - ensure that the container is accessible via - machinectl1 and shown by - tools such as ps1. If the container - does not run a service manager, it is recommended to set this option to - no. - - - - - - Instead of creating a transient scope unit to run the container in, simply use the service or - scope unit systemd-nspawn has been invoked in. If is set - this unit is registered with - systemd-machined8. This - switch should be used if systemd-nspawn is invoked from within a service unit, and the - service unit's sole purpose is to run a single systemd-nspawn container. This option is not - available if run from a user session. - Note that passing disables the effect of and - . Use and in - combination to disable any kind of unit allocation or registration with - systemd-machined. - - - - - - Control the architecture ("personality") - reported by - uname2 - in the container. Currently, only x86 and - x86-64 are supported. This is useful when - running a 32-bit container on a 64-bit host. If this setting - is not used, the personality reported in the container is the - same as the one reported on the host. - - - - - - - Turns off any status output by the tool - itself. When this switch is used, the only output from nspawn - will be the console output of the container OS - itself. - - - - - MODE - - Boots the container in volatile mode. When no - mode parameter is passed or when mode is specified as - , full volatile mode is enabled. This - means the root directory is mounted as a mostly unpopulated - tmpfs instance, and - /usr from the OS tree is mounted into it - in read-only mode (the system thus starts up with read-only OS - image, but pristine state and configuration, any changes - are lost on shutdown). When the mode parameter - is specified as , the OS tree is - mounted read-only, but /var is mounted as - a tmpfs instance into it (the system thus - starts up with read-only OS resources and configuration, but - pristine state, and any changes to the latter are lost on - shutdown). When the mode parameter is specified as - (the default), the whole OS tree is made - available writable. - - This option provides similar functionality for containers as the systemd.volatile= - kernel command line switch provides for host systems. See - kernel-command-line7 for - details. - - Note that enabling this setting will only work correctly with operating systems in the container that can - boot up with only /usr mounted, and are able to automatically populate - /var, and also /etc in case of - --volatile=yes. - - - - MODE - - Controls whether - systemd-nspawn shall search for and use - additional per-container settings from - .nspawn files. Takes a boolean or the - special values or - . - - If enabled (the default), a settings file named after the - machine (as specified with the - setting, or derived from the directory or image file name) - with the suffix .nspawn is searched in - /etc/systemd/nspawn/ and - /run/systemd/nspawn/. If it is found - there, its settings are read and used. If it is not found - there, it is subsequently searched in the same directory as the - image file or in the immediate parent of the root directory of - the container. In this case, if the file is found, its settings - will be also read and used, but potentially unsafe settings - are ignored. Note that in both these cases, settings on the - command line take precedence over the corresponding settings - from loaded .nspawn files, if both are - specified. Unsafe settings are considered all settings that - elevate the container's privileges or grant access to - additional resources such as files or directories of the - host. For details about the format and contents of - .nspawn files, consult - systemd.nspawn5. - - If this option is set to , the - file is searched, read and used the same way, however, the order of - precedence is reversed: settings read from the - .nspawn file will take precedence over - the corresponding command line options, if both are - specified. - - If this option is set to , the - file is searched, read and used the same way, but regardless - of being found in /etc/systemd/nspawn/, - /run/systemd/nspawn/ or next to the image - file or container root directory, all settings will take - effect, however, command line arguments still take precedence - over corresponding settings. - - If disabled, no .nspawn file is read - and no settings except the ones on the command line are in - effect. - - - - - - Configures support for notifications from the container's init process. - takes a boolean ( and ). - With option systemd-nspawn notifies systemd - with a READY=1 message when the init process is created. - With option systemd-nspawn waits for the - READY=1 message from the init process in the container - before sending its own to systemd. For more details about notifications - see sd_notify3). + Equivalent to . + - + + + Examples diff --git a/man/systemd-path.xml b/man/systemd-path.xml index b610af672..91a8786ab 100644 --- a/man/systemd-path.xml +++ b/man/systemd-path.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-portabled.service.xml b/man/systemd-portabled.service.xml index 8c5e4cc5a..3363a1d94 100644 --- a/man/systemd-portabled.service.xml +++ b/man/systemd-portabled.service.xml @@ -1,7 +1,6 @@ - - diff --git a/man/systemd-quotacheck.service.xml b/man/systemd-quotacheck.service.xml index c7cf23779..9ba9f3950 100644 --- a/man/systemd-quotacheck.service.xml +++ b/man/systemd-quotacheck.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-random-seed.service.xml b/man/systemd-random-seed.service.xml index dc5c88440..35c6e2fd0 100644 --- a/man/systemd-random-seed.service.xml +++ b/man/systemd-random-seed.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-rc-local-generator.xml b/man/systemd-rc-local-generator.xml index 3474a8f1d..514d1021d 100644 --- a/man/systemd-rc-local-generator.xml +++ b/man/systemd-rc-local-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-remount-fs.service.xml b/man/systemd-remount-fs.service.xml index 988a61740..a744df751 100644 --- a/man/systemd-remount-fs.service.xml +++ b/man/systemd-remount-fs.service.xml @@ -1,9 +1,8 @@ - - + + @@ -30,26 +29,31 @@ Description - systemd-remount-fs.service is an - early boot service that applies mount options listed in - fstab5 - to the root file system, the /usr file system, - and the kernel API file systems. This is required so that the - mount options of these file systems — which are pre-mounted by - the kernel, the initial RAM disk, container environments or system - manager code — are updated to those listed in - /etc/fstab. This service ignores normal file - systems and only changes the root file system (i.e. - /), /usr and the virtual - kernel API file systems such as /proc, - /sys or /dev. This - service executes no operation if /etc/fstab - does not exist or lists no entries for the mentioned file - systems. + systemd-remount-fs.service is an early boot service that applies mount options + listed in fstab5, or + gathered from the partition table (when + systemd-gpt-auto-generator8 + is active) to the root file system, the /usr file system, and the kernel API file + systems. This is required so that the mount options of these file systems — which are pre-mounted by the + kernel, the initial RAM disk, container environments or system manager code — are updated to those + configured in /etc/fstab and the other sources. This service ignores normal file + systems and only changes the root file system (i.e. /), /usr, + and the virtual kernel API file systems such as /proc, /sys or + /dev. This service executes no operation if no configuration is found + (/etc/fstab does not exist or lists no entries for the mentioned file systems, or + the partition table does not contain relevant entries). For a longer discussion of kernel API file systems see API File Systems. + + Note: systemd-remount-fs.service is usually pulled in by + systemd-fstab-generator8, + hence it is also affected by the kernel command line option fstab=, which may be used + to disable the generator. It may also pulled in by + systemd-gpt-auto-generator8, + which is affected by systemd.gpt_auto and other options. @@ -57,7 +61,9 @@ systemd1, fstab5, - mount8 + mount8, + systemd-fstab-generator8, + systemd-gpt-auto-generator8 diff --git a/man/systemd-resolved.service.xml b/man/systemd-resolved.service.xml index d7334e01e..807c3238b 100644 --- a/man/systemd-resolved.service.xml +++ b/man/systemd-resolved.service.xml @@ -1,10 +1,7 @@ - - - + @@ -249,7 +246,7 @@ systemd-resolved will flush all caches it maintains. Note that it should normally not be necessary to request this explicitly – except for debugging purposes – as systemd-resolved flushes the caches automatically anyway any time the host's network configuration changes. Sending this signal - to systemd-resolved is equivalent to the resolvectl --flush-caches + to systemd-resolved is equivalent to the resolvectl flush-caches command, however the latter is recommended since it operates in a synchronous way. @@ -263,7 +260,7 @@ should normally not be necessary to request this explicitly – except for debugging purposes – as systemd-resolved automatically forgets learnt information any time the DNS server configuration changes. Sending this signal to systemd-resolved is equivalent to the - resolvectl --reset-server-features command, however the latter is recommended since it + resolvectl reset-server-features command, however the latter is recommended since it operates in a synchronous way. diff --git a/man/systemd-rfkill.service.xml b/man/systemd-rfkill.service.xml index d8c2ee25f..bc3322cf2 100644 --- a/man/systemd-rfkill.service.xml +++ b/man/systemd-rfkill.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-run-generator.xml b/man/systemd-run-generator.xml index 20eb6916e..db84cf73a 100644 --- a/man/systemd-run-generator.xml +++ b/man/systemd-run-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-run.xml b/man/systemd-run.xml index 3c60340dc..8f7a6229c 100644 --- a/man/systemd-run.xml +++ b/man/systemd-run.xml @@ -1,10 +1,7 @@ - - - + + @@ -319,6 +316,18 @@ + + + + + Defines a trigger based on system clock jumps or timezone changes for starting the + specified command. See OnClockChange= and OnTimezoneChange= in + systemd.timer5. These + options are shortcuts for --timer-property=OnClockChange=yes and + --timer-property=OnTimezoneChange=yes. These options may not be combined with + or . + + diff --git a/man/systemd-sleep.conf.xml b/man/systemd-sleep.conf.xml index af61947c5..3311a046c 100644 --- a/man/systemd-sleep.conf.xml +++ b/man/systemd-sleep.conf.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/systemd-socket-activate.xml b/man/systemd-socket-activate.xml index c3707050d..f3dbb47a6 100644 --- a/man/systemd-socket-activate.xml +++ b/man/systemd-socket-activate.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/systemd-socket-proxyd.xml b/man/systemd-socket-proxyd.xml index 1869465a3..a72ac1bbc 100644 --- a/man/systemd-socket-proxyd.xml +++ b/man/systemd-socket-proxyd.xml @@ -1,10 +1,8 @@ - - + + diff --git a/man/systemd-suspend.service.xml b/man/systemd-suspend.service.xml index cef7adb67..2310e6f52 100644 --- a/man/systemd-suspend.service.xml +++ b/man/systemd-suspend.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-sysctl.service.xml b/man/systemd-sysctl.service.xml index 271240223..7b04f4b70 100644 --- a/man/systemd-sysctl.service.xml +++ b/man/systemd-sysctl.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-system-update-generator.xml b/man/systemd-system-update-generator.xml index a7b62b528..d816c0b2c 100644 --- a/man/systemd-system-update-generator.xml +++ b/man/systemd-system-update-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-system.conf.xml b/man/systemd-system.conf.xml index 27242b3a5..d23b3fb45 100644 --- a/man/systemd-system.conf.xml +++ b/man/systemd-system.conf.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/systemd-sysusers.xml b/man/systemd-sysusers.xml index 6c9b921f0..cc0262569 100644 --- a/man/systemd-sysusers.xml +++ b/man/systemd-sysusers.xml @@ -1,10 +1,7 @@ - - - + @@ -98,7 +95,7 @@ placing /etc/sysusers.d/radvd.conf or even /etc/sysusers.d/00-overrides.conf. - Note that this is the expanded from, and when used in a package, this + Note that this is the expanded form, and when used in a package, this would be written using a macro with "radvd" and a file containing the configuration line as arguments. diff --git a/man/systemd-sysv-generator.xml b/man/systemd-sysv-generator.xml index 0b07cc3cb..795d9eebe 100644 --- a/man/systemd-sysv-generator.xml +++ b/man/systemd-sysv-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-time-wait-sync.service.xml b/man/systemd-time-wait-sync.service.xml index 5667c886e..d51b272ed 100644 --- a/man/systemd-time-wait-sync.service.xml +++ b/man/systemd-time-wait-sync.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-timedated.service.xml b/man/systemd-timedated.service.xml index e334e8a84..3626e8bc5 100644 --- a/man/systemd-timedated.service.xml +++ b/man/systemd-timedated.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-timesyncd.service.xml b/man/systemd-timesyncd.service.xml index cdea1067a..3dff63779 100644 --- a/man/systemd-timesyncd.service.xml +++ b/man/systemd-timesyncd.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-tmpfiles.xml b/man/systemd-tmpfiles.xml index 9978d95ca..f05e5ea2e 100644 --- a/man/systemd-tmpfiles.xml +++ b/man/systemd-tmpfiles.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-tty-ask-password-agent.xml b/man/systemd-tty-ask-password-agent.xml index 75ab0322d..8d9e9e0c1 100644 --- a/man/systemd-tty-ask-password-agent.xml +++ b/man/systemd-tty-ask-password-agent.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-udevd.service.xml b/man/systemd-udevd.service.xml index 330700d7d..cf8087ccb 100644 --- a/man/systemd-udevd.service.xml +++ b/man/systemd-udevd.service.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd-update-done.service.xml b/man/systemd-update-done.service.xml index fa88e66f9..ad412691a 100644 --- a/man/systemd-update-done.service.xml +++ b/man/systemd-update-done.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-update-utmp.service.xml b/man/systemd-update-utmp.service.xml index a3822ac98..e1059ec73 100644 --- a/man/systemd-update-utmp.service.xml +++ b/man/systemd-update-utmp.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-user-sessions.service.xml b/man/systemd-user-sessions.service.xml index 52fc0d716..ab33d469a 100644 --- a/man/systemd-user-sessions.service.xml +++ b/man/systemd-user-sessions.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-vconsole-setup.service.xml b/man/systemd-vconsole-setup.service.xml index 08c5d8116..268e69c0e 100644 --- a/man/systemd-vconsole-setup.service.xml +++ b/man/systemd-vconsole-setup.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-veritysetup-generator.xml b/man/systemd-veritysetup-generator.xml index f9f645fe9..305dda4b8 100644 --- a/man/systemd-veritysetup-generator.xml +++ b/man/systemd-veritysetup-generator.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-veritysetup@.service.xml b/man/systemd-veritysetup@.service.xml index 7701409d3..2c71c3e70 100644 --- a/man/systemd-veritysetup@.service.xml +++ b/man/systemd-veritysetup@.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd-volatile-root.service.xml b/man/systemd-volatile-root.service.xml index 651832f36..440e9eac7 100644 --- a/man/systemd-volatile-root.service.xml +++ b/man/systemd-volatile-root.service.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd.automount.xml b/man/systemd.automount.xml index 2e55ad0b6..48deb0220 100644 --- a/man/systemd.automount.xml +++ b/man/systemd.automount.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.device.xml b/man/systemd.device.xml index 014470410..ff7ab9ccc 100644 --- a/man/systemd.device.xml +++ b/man/systemd.device.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.dnssd.xml b/man/systemd.dnssd.xml index dc00591fc..1ac760eec 100644 --- a/man/systemd.dnssd.xml +++ b/man/systemd.dnssd.xml @@ -1,9 +1,7 @@ - - - + @@ -36,15 +34,16 @@ The main network service file must have the extension .dnssd; other extensions are ignored. - The .dnssd files are read from the files located in the system - network directory /usr/lib/systemd/dnssd, the volatile runtime network - directory /run/systemd/dnssd and the local administration network - directory /etc/systemd/dnssd. All configuration files are collectively - sorted and processed in lexical order, regardless of the directories in which they live. - However, files with identical filenames replace each other. Files in /etc - have the highest priority, files in /run take precedence over files with - the same name in /usr/lib. This can be used to override a system-supplied - configuration file with a local file if needed. + The .dnssd files are read from the files located in the system network + directories /usr/lib/systemd/dnssd and + /usr/local/lib/systemd/dnssd, the volatile runtime network directory + /run/systemd/dnssd and the local administration network directory + /etc/systemd/dnssd. All configuration files are collectively sorted and processed in + lexical order, regardless of the directories in which they live. However, files with identical filenames + replace each other. Files in /etc have the highest priority, files in + /run take precedence over files with the same name in + /usr/lib. This can be used to override a system-supplied configuration file with a + local file if needed. Along with the network service file foo.dnssd, a "drop-in" directory foo.dnssd.d/ may exist. All files with the suffix @@ -52,14 +51,12 @@ parsed. This is useful to alter or add configuration settings, without having to modify the main configuration file. Each drop-in file must have appropriate section headers. - In addition to /etc/systemd/dnssd, drop-in .d - directories can be placed in /usr/lib/systemd/dnssd or - /run/systemd/dnssd directories. Drop-in files in - /etc take precedence over those in /run which in turn - take precedence over those in /usr/lib. Drop-in files under any of these - directories take precedence over the main network service file wherever located. (Of course, since - /run is temporary and /usr/lib is for vendors, it is - unlikely drop-ins should be used in either of those places.) + In addition to /etc/systemd/dnssd, drop-in .d directories + can be placed in /usr/lib/systemd/dnssd or /run/systemd/dnssd + directories. Drop-in files in /etc take precedence over those in + /run which in turn take precedence over those in /usr/lib or + /usr/local/lib. Drop-in files under any of these directories take precedence over + the main network service file wherever located. diff --git a/man/systemd.environment-generator.xml b/man/systemd.environment-generator.xml index 4595a9957..a806f7bcf 100644 --- a/man/systemd.environment-generator.xml +++ b/man/systemd.environment-generator.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/systemd.exec.xml b/man/systemd.exec.xml index 0248c3a8e..688147ea3 100644 --- a/man/systemd.exec.xml +++ b/man/systemd.exec.xml @@ -1,12 +1,9 @@ - + - - - + systemd.exec systemd @@ -112,7 +109,9 @@ dependencies to be added to the unit (see above). The MountAPIVFS= and PrivateUsers= settings are particularly useful - in conjunction with RootDirectory=. For details, see below. + in conjunction with RootDirectory=. For details, see below. + + @@ -126,14 +125,17 @@ url="https://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/">Discoverable Partitions Specification. - When DevicePolicy= is set to closed or strict, - or set to auto and DeviceAllow= is set, then this setting adds - /dev/loop-control with rw mode, block-loop and - block-blkext with rwm mode to DeviceAllow=. See + When DevicePolicy= is set to closed or + strict, or set to auto and DeviceAllow= is + set, then this setting adds /dev/loop-control with rw mode, + block-loop and block-blkext with rwm mode + to DeviceAllow=. See systemd.resource-control5 for the details about DevicePolicy= or DeviceAllow=. Also, see - PrivateDevices= below, as it may change the setting of DevicePolicy=. - + PrivateDevices= below, as it may change the setting of + DevicePolicy=. + + @@ -147,7 +149,9 @@ will be a 1:1 copy of the host's, and include these three mounts. Note that the /dev file system of the host is bind mounted if this option is used without PrivateDevices=. To run the service with a private, minimal version of /dev/, combine this option with - PrivateDevices=. + PrivateDevices=. + + @@ -174,7 +178,9 @@ This option is particularly useful when RootDirectory=/RootImage= is used. In this case the source path refers to a path on the host file system, while the destination path - refers to a path below the root directory of the unit. + refers to a path below the root directory of the unit. + + @@ -183,6 +189,8 @@ Credentials + + @@ -217,40 +225,51 @@ DynamicUser= - Takes a boolean parameter. If set, a UNIX user and group pair is allocated dynamically when the - unit is started, and released as soon as it is stopped. The user and group will not be added to - /etc/passwd or /etc/group, but are managed transiently during - runtime. The nss-systemd8 - glibc NSS module provides integration of these dynamic users/groups into the system's user and group + Takes a boolean parameter. If set, a UNIX user and group pair is allocated + dynamically when the unit is started, and released as soon as it is stopped. The user and group will + not be added to /etc/passwd or /etc/group, but are managed + transiently during runtime. The + nss-systemd8 glibc + NSS module provides integration of these dynamic users/groups into the system's user and group databases. The user and group name to use may be configured via User= and - Group= (see above). If these options are not used and dynamic user/group allocation is - enabled for a unit, the name of the dynamic user/group is implicitly derived from the unit name. If the unit - name without the type suffix qualifies as valid user name it is used directly, otherwise a name incorporating a - hash of it is used. If a statically allocated user or group of the configured name already exists, it is used - and no dynamic user/group is allocated. Note that if User= is specified and the static group - with the name exists, then it is required that the static user with the name already exists. Similarly, if - Group= is specified and the static user with the name exists, then it is required that the - static group with the name already exists. Dynamic users/groups are allocated from the UID/GID range - 61184…65519. It is recommended to avoid this range for regular system or login users. At any point in time - each UID/GID from this range is only assigned to zero or one dynamically allocated users/groups in - use. However, UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running - as part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by these - users/groups around, as a different unit might get the same UID/GID assigned later on, and thus gain access to - these files or directories. If DynamicUser= is enabled, RemoveIPC=, - PrivateTmp= are implied. This ensures that the lifetime of IPC objects and temporary files - created by the executed processes is bound to the runtime of the service, and hence the lifetime of the dynamic - user/group. Since /tmp and /var/tmp are usually the only - world-writable directories on a system this ensures that a unit making use of dynamic user/group allocation - cannot leave files around after unit termination. Moreover ProtectSystem=strict and - ProtectHome=read-only are implied, thus prohibiting the service to write to arbitrary file - system locations. In order to allow the service to write to certain directories, they have to be whitelisted - using ReadWritePaths=, but care must be taken so that UID/GID recycling doesn't create - security issues involving files created by the service. Use RuntimeDirectory= (see below) in - order to assign a writable runtime directory to a service, owned by the dynamic user/group and removed - automatically when the unit is terminated. Use StateDirectory=, - CacheDirectory= and LogsDirectory= in order to assign a set of writable - directories for specific purposes to the service in a way that they are protected from vulnerabilities due to - UID reuse (see below). Defaults to off. + Group= (see above). If these options are not used and dynamic user/group + allocation is enabled for a unit, the name of the dynamic user/group is implicitly derived from the + unit name. If the unit name without the type suffix qualifies as valid user name it is used directly, + otherwise a name incorporating a hash of it is used. If a statically allocated user or group of the + configured name already exists, it is used and no dynamic user/group is allocated. Note that if + User= is specified and the static group with the name exists, then it is required + that the static user with the name already exists. Similarly, if Group= is + specified and the static user with the name exists, then it is required that the static group with + the name already exists. Dynamic users/groups are allocated from the UID/GID range 61184…65519. It is + recommended to avoid this range for regular system or login users. At any point in time each UID/GID + from this range is only assigned to zero or one dynamically allocated users/groups in use. However, + UID/GIDs are recycled after a unit is terminated. Care should be taken that any processes running as + part of a unit for which dynamic users/groups are enabled do not leave files or directories owned by + these users/groups around, as a different unit might get the same UID/GID assigned later on, and thus + gain access to these files or directories. If DynamicUser= is enabled, + RemoveIPC=, PrivateTmp= are implied. This ensures that the + lifetime of IPC objects and temporary files created by the executed processes is bound to the runtime + of the service, and hence the lifetime of the dynamic user/group. Since /tmp and + /var/tmp are usually the only world-writable directories on a system this + ensures that a unit making use of dynamic user/group allocation cannot leave files around after unit + termination. Furthermore NoNewPrivileges= and RestrictSUIDSGID= + are implicitly enabled to ensure that processes invoked cannot take benefit or create SUID/SGID files + or directories. Moreover ProtectSystem=strict and + ProtectHome=read-only are implied, thus prohibiting the service to write to + arbitrary file system locations. In order to allow the service to write to certain directories, they + have to be whitelisted using ReadWritePaths=, but care must be taken so that + UID/GID recycling doesn't create security issues involving files created by the service. Use + RuntimeDirectory= (see below) in order to assign a writable runtime directory to a + service, owned by the dynamic user/group and removed automatically when the unit is terminated. Use + StateDirectory=, CacheDirectory= and + LogsDirectory= in order to assign a set of writable directories for specific + purposes to the service in a way that they are protected from vulnerabilities due to UID reuse (see + below). If this option is enabled, care should be taken that the unit's processes do not get access + to directories outside of these explicitly configured and managed ones. Specifically, do not use + BindPaths= and be careful with AF_UNIX file descriptor + passing for directory file descriptors, as this would permit processes to create files or directories + owned by the dynamic user/group that are not subject to the lifecycle and access guarantees of the + service. Defaults to off. @@ -297,6 +316,8 @@ Capabilities + + @@ -360,19 +381,21 @@ CapabilityBoundingSet=~CAP_B CAP_C NoNewPrivileges= - Takes a boolean argument. If true, ensures that the service process and all its children can - never gain new privileges through execve() (e.g. via setuid or setgid bits, or filesystem - capabilities). This is the simplest and most effective way to ensure that a process and its children can never - elevate privileges again. Defaults to false, but certain settings override this and ignore the value of this - setting. This is the case when SystemCallFilter=, - SystemCallArchitectures=, RestrictAddressFamilies=, - RestrictNamespaces=, PrivateDevices=, - ProtectKernelTunables=, ProtectKernelModules=, - MemoryDenyWriteExecute=, RestrictRealtime=, or - LockPersonality= are specified. Note that even if this setting is overridden by them, - systemctl show shows the original value of this setting. Also see - No New Privileges - Flag. + Takes a boolean argument. If true, ensures that the service process and all its + children can never gain new privileges through execve() (e.g. via setuid or + setgid bits, or filesystem capabilities). This is the simplest and most effective way to ensure that + a process and its children can never elevate privileges again. Defaults to false, but certain + settings override this and ignore the value of this setting. This is the case when + SystemCallFilter=, SystemCallArchitectures=, + RestrictAddressFamilies=, RestrictNamespaces=, + PrivateDevices=, ProtectKernelTunables=, + ProtectKernelModules=, MemoryDenyWriteExecute=, + RestrictRealtime=, RestrictSUIDSGID=, + DynamicUser= or LockPersonality= are specified. Note that even + if this setting is overridden by them, systemctl show shows the original value of + this setting. Also see No New Privileges + Flag. @@ -393,6 +416,9 @@ CapabilityBoundingSet=~CAP_B CAP_C Mandatory Access Control + + + @@ -806,7 +832,9 @@ CapabilityBoundingSet=~CAP_B CAP_C 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 DynamicUser= is set. This setting cannot ensure protection in all cases. In general it has the same limitations as ReadOnlyPaths=, - see below. + see below. + + @@ -827,40 +855,40 @@ CapabilityBoundingSet=~CAP_B CAP_C - Locations - for system - for users - Environment variable + Directory + Below path for system units + Below path for user units + Environment variable set RuntimeDirectory= - /run + /run/ $XDG_RUNTIME_DIR $RUNTIME_DIRECTORY StateDirectory= - /var/lib + /var/lib/ $XDG_CONFIG_HOME $STATE_DIRECTORY CacheDirectory= - /var/cache + /var/cache/ $XDG_CACHE_HOME $CACHE_DIRECTORY LogsDirectory= - /var/log - $XDG_CONFIG_HOME/log + /var/log/ + $XDG_CONFIG_HOME/log/ $LOGS_DIRECTORY ConfigurationDirectory= - /etc + /etc/ $XDG_CONFIG_HOME $CONFIGURATION_DIRECTORY @@ -868,10 +896,10 @@ CapabilityBoundingSet=~CAP_B CAP_C - In case of RuntimeDirectory= the lowest subdirectories are removed when the unit is - stopped. It is possible to preserve the specified directories in this case if - RuntimeDirectoryPreserve= is configured to or - (see below). The directories specified with StateDirectory=, + In case of RuntimeDirectory= the innermost subdirectories are removed when + the unit is stopped. It is possible to preserve the specified directories in this case if + RuntimeDirectoryPreserve= is configured to or + (see below). The directories specified with StateDirectory=, CacheDirectory=, LogsDirectory=, ConfigurationDirectory= are not removed when the unit is stopped. @@ -1000,7 +1028,9 @@ StateDirectory=aaa/bbb ccc 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 CapabilityBoundingSet=~CAP_SYS_ADMIN or - SystemCallFilter=~@mount. + SystemCallFilter=~@mount. + + @@ -1023,7 +1053,9 @@ StateDirectory=aaa/bbb ccc TemporaryFileSystem=/var:ro BindReadOnlyPaths=/var/lib/systemd then the invoked processes by the unit cannot see any files or directories under /var except for - /var/lib/systemd or its contents. + /var/lib/systemd or its contents. + + @@ -1048,7 +1080,9 @@ BindReadOnlyPaths=/var/lib/systemd Note that the implementation of this setting might be impossible (for example if mount namespaces are not available), and the unit should be written in a way that does not solely rely on this setting for - security. + security. + + @@ -1078,7 +1112,9 @@ BindReadOnlyPaths=/var/lib/systemd Note that the implementation of this setting might be impossible (for example if mount namespaces are not available), and the unit should be written in a way that does not solely rely on this setting for - security. + security. + + @@ -1100,7 +1136,33 @@ BindReadOnlyPaths=/var/lib/systemd 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 - security. + security. + + When this option is used on a socket unit any sockets bound on behalf of this unit will be + bound within a private network namespace. This may be combined with + JoinsNamespaceOf= to listen on sockets inside of network namespaces of other + services. + + + + + + NetworkNamespacePath= + + Takes an absolute file system path refererring to a Linux network namespace + pseudo-file (i.e. a file like /proc/$PID/ns/net or a bind mount or symlink to + one). When set the invoked processes are added to the network namespace referenced by that path. The + path has to point to a valid namespace file at the moment the processes are forked off. If this + option is used PrivateNetwork= has no effect. If this option is used together with + JoinsNamespaceOf= then it only has an effect if this unit is started before any of + the listed units that have PrivateNetwork= or + NetworkNamespacePath= configured, as otherwise the network namespace of those + units is reused. + + When this option is used on a socket unit any sockets bound on behalf of this unit will be + bound within the specified network namespace. + + @@ -1126,7 +1188,26 @@ BindReadOnlyPaths=/var/lib/systemd Note that the implementation of this setting might be impossible (for example if user namespaces are not available), and the unit should be written in a way that does not solely rely on this setting for - security. + security. + + + + + + ProtectHostname= + + Takes a boolean argument. When set, sets up a new UTS namespace for the executed + processes. In addition, changing hostname or domainname is prevented. Defaults to off. + + Note that the implementation of this setting might be impossible (for example if UTS namespaces + are not available), and the unit should be written in a way that does not solely rely on this setting + for security. + + Note that when this option is enabled for a service hostname changes no longer propagate from + the system into the service, it is hence not suitable for services that need to take notice of system + hostname changes dynamically. + + @@ -1147,7 +1228,9 @@ BindReadOnlyPaths=/var/lib/systemd option does not prevent indirect changes to kernel tunables effected by IPC calls to other processes. However, InaccessiblePaths= may be used to make relevant IPC file system objects inaccessible. If ProtectKernelTunables= is set, MountAPIVFS=yes is - implied. + implied. + + @@ -1166,7 +1249,9 @@ BindReadOnlyPaths=/var/lib/systemd kernel.modules_disabled mechanism and /proc/sys/kernel/modules_disabled documentation. If turned on and if running in user mode, or in system mode, but without the CAP_SYS_ADMIN capability (e.g. setting - User=), NoNewPrivileges=yes is implied. + User=), NoNewPrivileges=yes is implied. + + @@ -1179,7 +1264,9 @@ BindReadOnlyPaths=/var/lib/systemd it is hence recommended to turn this on for most services. For this setting the same restrictions regarding mount propagation and privileges apply as for ReadOnlyPaths= and related calls, see above. Defaults to off. If ProtectControlGroups= is set, MountAPIVFS=yes - is implied. + is implied. + + @@ -1309,6 +1396,23 @@ RestrictNamespaces=~cgroup net that actually require them. Defaults to off. + + RestrictSUIDSGID= + + Takes a boolean argument. If set, any attempts to set the set-user-ID (SUID) or + set-group-ID (SGID) bits on files or directories will be denied (for details on these bits see + inode7). If + running in user mode, or in system mode, but without the CAP_SYS_ADMIN + capability (e.g. setting User=), NoNewPrivileges=yes is + implied. As the SUID/SGID bits are mechanisms to elevate privileges, and allows users to acquire the + identity of other users, it is recommended to restrict creation of SUID/SGID files to the few + programs that actually require them. Note that this restricts marking of any type of file system + object with these bits, including both regular files and directories (where the SGID is a different + meaning than for files, see documentation). This option is implied if DynamicUser= + is enabled. Defaults to off. + + RemoveIPC= @@ -1318,7 +1422,9 @@ RestrictNamespaces=~cgroup net DynamicUser= are used. It has no effect on IPC objects owned by the root user. Specifically, this removes System V semaphores, as well as System V and POSIX shared memory segments and message queues. If multiple units use the same user or group the IPC objects are removed when the last of these units is - stopped. This setting is implied if DynamicUser= is set. + stopped. This setting is implied if DynamicUser= is set. + + @@ -1351,7 +1457,9 @@ RestrictNamespaces=~cgroup net ProtectHome=, ReadOnlyPaths=, InaccessiblePaths=, ReadWritePaths=, … — also enable file system namespacing in a fashion equivalent to this option. Hence it is primarily useful to explicitly request this behaviour if none of the other settings are - used. + used. + + @@ -1380,7 +1488,8 @@ RestrictNamespaces=~cgroup net Usually, it is best to leave this setting unmodified, and use higher level file system namespacing options instead, in particular PrivateMounts=, see above. - + + @@ -2468,6 +2577,18 @@ StandardInputData=SWNrIHNpdHplIGRhIHVuJyBlc3NlIEtsb3BzLAp1ZmYgZWVtYWwga2xvcHAncy + + + $PIDFILE + + The path to the configured PID file, in case the process is forked off on behalf of a + service that uses the PIDFile= setting, see + systemd.service5 + for details. Service code may use this environment variable to automatically generate a PID file at + the location configured in the unit file. This field is set to an absolute path in the file + system. + + For system services, when PAMName= is enabled and pam_systemd is part diff --git a/man/systemd.generator.xml b/man/systemd.generator.xml index 5007563e0..c77afda0f 100644 --- a/man/systemd.generator.xml +++ b/man/systemd.generator.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + @@ -196,12 +193,15 @@ - It is a good idea to use the SourcePath= directive - in generated unit files to specify the source configuration file you are - generating the unit from. This makes things more easily understood by the - user and also has the benefit that systemd can warn the user about - configuration files that changed on disk but have not been read yet by - systemd. + The generator should always include its own name in a comment at the top of the generated file, + so that the user can easily figure out which component created or amended a particular unit. + + The SourcePath= directive should be used in generated files to specify the + source configuration file they are generated from. This makes things more easily understood by the + user and also has the benefit that systemd can warn the user about configuration files that changed + on disk but have not been read yet by systemd. The SourcePath= value does not have + to be a file in a physical filesystem. For example, in the common case of the generator looking at + the kernel command line, should be used. diff --git a/man/systemd.journal-fields.xml b/man/systemd.journal-fields.xml index 76e1de72c..960b2ec63 100644 --- a/man/systemd.journal-fields.xml +++ b/man/systemd.journal-fields.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.kill.xml b/man/systemd.kill.xml index 1b4a4a84a..1f9d622ce 100644 --- a/man/systemd.kill.xml +++ b/man/systemd.kill.xml @@ -1,10 +1,7 @@ - - - + @@ -87,22 +84,17 @@ group and the control group continues to exist after stop unless it is empty. - Processes will first be terminated via - SIGTERM (unless the signal to send is - changed via KillSignal=). Optionally, this - is immediately followed by a SIGHUP (if - enabled with SendSIGHUP=). If then, after a - delay (configured via the TimeoutStopSec= - option), processes still remain, the termination request is - repeated with the SIGKILL signal or the - signal specified via FinalKillSignal= (unless - this is disabled via the SendSIGKILL= - option). See - kill2 - for more information. + Processes will first be terminated via SIGTERM (unless the signal to send + is changed via KillSignal=). Optionally, this is immediately followed by a + SIGHUP (if enabled with SendSIGHUP=). If processes still + remain after the main process of a unit has exited or the delay configured via the + TimeoutStopSec= has passed, the termination request is repeated with the + SIGKILL signal or the signal specified via FinalKillSignal= + (unless this is disabled via the SendSIGKILL= option). See + kill2 for more + information. - Defaults to - . + Defaults to . @@ -139,8 +131,11 @@ SIGKILL (or the signal specified by FinalKillSignal=) to remaining processes after a timeout, if the normal shutdown procedure left - processes of the service around. Takes a boolean value. - Defaults to "yes". + processes of the service around. When disabled, a + KillMode= of control-group + or mixed service will not restart if + processes from prior services exist within the control group. + Takes a boolean value. Defaults to "yes". diff --git a/man/systemd.link.xml b/man/systemd.link.xml index 178f9b81a..af9799e8c 100644 --- a/man/systemd.link.xml +++ b/man/systemd.link.xml @@ -1,10 +1,7 @@ - - - + @@ -116,52 +113,55 @@ Host= - Matches against the hostname or machine - ID of the host. See ConditionHost= in + Matches against the hostname or machine ID of the host. See ConditionHost= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. + Virtualization= - Checks whether the system is executed in - a virtualized environment and optionally test - whether it is a specific implementation. See - ConditionVirtualization= in + Checks whether the system is executed in a virtualized environment and optionally test + whether it is a specific implementation. See ConditionVirtualization= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. + KernelCommandLine= - Checks whether a specific kernel command line option - is set (or if prefixed with the exclamation mark unset). See + Checks whether a specific kernel command line option is set. See ConditionKernelCommandLine= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. + + + + + KernelVersion= + + Checks whether the kernel version (as reported by uname -r) matches a certain + expression. See ConditionKernelVersion= in + systemd.unit5 for + details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. + - - KernelVersion= - - Checks whether the kernel version (as reported by uname -r) matches a certain - expression (or if prefixed with the exclamation mark does not match it). See - ConditionKernelVersion= in - systemd.unit5 for - details. - - - Architecture= - Checks whether the system is running on a specific - architecture. See ConditionArchitecture= - in + Checks whether the system is running on a specific architecture. See + ConditionArchitecture= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. + @@ -242,9 +242,11 @@ An ordered, space-separated list of policies by which the interface name should be set. NamePolicy= may be disabled by specifying on the - kernel command line. Each of the policies may fail, and the first successful one is used. The name + kernel command line. Each of the policies may fail, and the first successful one is used. The name is not set directly, but is exported to udev as the property , which - is, by default, used by a udev rule to set NAME. The available policies are: + is, by default, used by a + udev7, + rule to set NAME. The available policies are: @@ -312,11 +314,10 @@ Name= - The interface name to use in case all the - policies specified in - NamePolicy= fail, or in case - NamePolicy= is missing or - disabled. + The interface name to use. This option has lower precedence than + NamePolicy=, so for this setting to take effect, NamePolicy= + must either be unset, empty, disabled, or all policies configured there must fail. Also see the + example below with Name=dmz0. Note that specifying a name that the kernel might use for another interface (for example eth0) is dangerous because the @@ -618,8 +619,7 @@ MACAddressPolicy=persistent /etc/systemd/network/10-dmz.link - This example assigns the fixed name - dmz0 to the interface with the MAC address + This example assigns the fixed name dmz0 to the interface with the MAC address 00:a0:de:63:7a:e6: [Match] @@ -627,6 +627,62 @@ MACAddress=00:a0:de:63:7a:e6 [Link] Name=dmz0 + + NamePolicy= is not set, so Name= takes effect. We use the + 10- prefix to order this file early in the list. Note that it needs to before + 99-link, i.e. it needs a numerical prefix, to have any effect at all. + + + + Debugging <varname>NamePolicy=</varname> assignments + + $ sudo SYSTEMD_LOG_LEVEL=debug udevadm test-builtin net_setup_link /sys/class/net/hub0 +… +Parsed configuration file /usr/lib/systemd/network/99-default.link +Parsed configuration file /etc/systemd/network/10-eth0.link +ID_NET_DRIVER=cdc_ether +Config file /etc/systemd/network/10-eth0.link applies to device hub0 +link_config: autonegotiation is unset or enabled, the speed and duplex are not writable. +hub0: Device has name_assign_type=4 +Using default interface naming scheme 'v240'. +hub0: Policies didn't yield a name, using specified Name=hub0. +ID_NET_LINK_FILE=/etc/systemd/network/10-eth0.link +ID_NET_NAME=hub0 +… + + Explicit Name= configuration wins in this case. + + sudo SYSTEMD_LOG_LEVEL=debug udevadm test-builtin net_setup_link /sys/class/net/enp0s31f6 +… +Parsed configuration file /usr/lib/systemd/network/99-default.link +Parsed configuration file /etc/systemd/network/10-eth0.link +Created link configuration context. +ID_NET_DRIVER=e1000e +Config file /usr/lib/systemd/network/99-default.link applies to device enp0s31f6 +link_config: autonegotiation is unset or enabled, the speed and duplex are not writable. +enp0s31f6: Device has name_assign_type=4 +Using default interface naming scheme 'v240'. +enp0s31f6: Policy *keep*: keeping existing userspace name +enp0s31f6: Device has addr_assign_type=0 +enp0s31f6: MAC on the device already matches policy *persistent* +ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link +… + + + In this case, the interface was already renamed, so the policy specified as + the first option in 99-default.link means that the existing name is + preserved. If was removed, or if were in boot before the renaming has happened, + we might get the following instead: + + enp0s31f6: Policy *path* yields "enp0s31f6". +enp0s31f6: Device has addr_assign_type=0 +enp0s31f6: MAC on the device already matches policy *persistent* +ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link +ID_NET_NAME=enp0s31f6 +… + + + Please note that the details of output are subject to change. diff --git a/man/systemd.mount.xml b/man/systemd.mount.xml index 61e97b14f..d0ccd39e3 100644 --- a/man/systemd.mount.xml +++ b/man/systemd.mount.xml @@ -1,10 +1,7 @@ - - - + @@ -86,6 +83,11 @@ disabled. For a longer discussion see API File Systems. + + The + systemd-mount1 command + allows creating .mount and .automount units dynamically and + transiently from the command line. @@ -538,7 +540,8 @@ proc5, mount8, systemd-fstab-generator8, - systemd.directives7 + systemd.directives7, + systemd-mount1 diff --git a/man/systemd.netdev.xml b/man/systemd.netdev.xml index 74281f2d0..1836b5fe0 100644 --- a/man/systemd.netdev.xml +++ b/man/systemd.netdev.xml @@ -1,10 +1,7 @@ - - - + @@ -101,9 +98,7 @@ A Level 2 GRE tunnel over IPv4. erspan - 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. + 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. ip6gre A Level 3 GRE tunnel over IPv6. @@ -153,6 +148,9 @@ geneve A GEneric NEtwork Virtualization Encapsulation (GENEVE) netdev driver. + l2tp + A Layer 2 Tunneling Protocol (L2TP) is a tunneling protocol used to support virtual private networks (VPNs) or as part of the delivery of services by ISPs. It does not provide any encryption or confidentiality by itself + vrf A Virtual Routing and Forwarding (VRF) interface to create separate routing and forwarding domains. @@ -190,53 +188,55 @@ Host= - Matches against the hostname or machine ID of the - host. See ConditionHost= in + Matches against the hostname or machine ID of the host. See + ConditionHost= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. Virtualization= - Checks whether the system is executed in a virtualized - environment and optionally test whether it is a specific - implementation. See - ConditionVirtualization= in + Checks whether the system is executed in a virtualized environment and optionally test + whether it is a specific implementation. See ConditionVirtualization= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. KernelCommandLine= - Checks whether a specific kernel command line option - is set (or if prefixed with the exclamation mark unset). See + Checks whether a specific kernel command line option is set. See ConditionKernelCommandLine= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. KernelVersion= - Checks whether the kernel version (as reported by uname -r) matches a certain - expression (or if prefixed with the exclamation mark does not match it). See - ConditionKernelVersion= in - systemd.unit5 for details. + Checks whether the kernel version (as reported by uname -r) matches a + certain expression. See ConditionKernelVersion= in + systemd.unit5 + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. Architecture= - Checks whether the system is running on a specific - architecture. See ConditionArchitecture= in + Checks whether the system is running on a specific architecture. See + ConditionArchitecture= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. @@ -739,6 +739,118 @@ + + [L2TP] Section Options + The [L2TP] section only applies for + netdevs of kind l2tp, and accepts the + following keys: + + + + TunnelId= + + Specifies the tunnel id. The value used must match the PeerTunnelId= value being used at the peer. + Ranges a number between 1 and 4294967295). This option is compulsory. + + + + PeerTunnelId= + + Specifies the peer tunnel id. The value used must match the PeerTunnelId= value being used at the peer. + Ranges a number between 1 and 4294967295). This option is compulsory. + + + + Remote= + + Specifies the IP address of the remote peer. This option is compulsory. + + + + Local= + + Specifies the IP address of the local interface. Takes an IP address, or the special values + auto, static, or dynamic. When an address + is set, then the local interface must have the address. If auto, then one of the + addresses on the local interface is used. Similarly, if static or + dynamic is set, then one of the static or dynamic addresses on the local + interface is used. Defaults to auto. + + + + EncapsulationType= + + Specifies the encapsulation type of the tunnel. Takes one of udp or ip. + + + + UDPSourcePort= + + Specifies the UDP source port to be used for the tunnel. When UDP encapsulation is selected it's mandotory. Ignored when ip + encapsulation is selected. + + + + DestinationPort= + + Specifies destination port. When UDP encapsulation is selected it's mandotory. Ignored when ip + encapsulation is selected. + + + + UDPChecksum= + + Takes a boolean. When true, specifies if UDP checksum is calculated for transmitted packets over IPv4. + + + + UDP6ZeroChecksumTx= + + Takes a boolean. When true, skip UDP checksum calculation for transmitted packets over IPv6. + + + + UDP6ZeroChecksumRx= + + Takes a boolean. When true, allows incoming UDP packets over IPv6 with zero checksum field. + + + + + + [L2TPSession] Section Options + The [L2TPSession] section only applies for + netdevs of kind l2tp, and accepts the + following keys: + + + Name= + + Specifies the name of the sesssion. This option is compulsory. + + + + SessionId= + + Specifies the sesssion id. The value used must match the SessionId= value being used at the peer. + Ranges a number between 1 and 4294967295). This option is compulsory. + + + + PeerSessionId= + + Specifies the peer session id. The value used must match the PeerSessionId= value being used at the peer. + Ranges a number between 1 and 4294967295). This option is compulsory. + + + + Layer2SpecificHeader= + + Specifies layer2specific header type of the session. One of none or default. Defaults to default. + + + + [Tunnel] Section Options @@ -751,22 +863,24 @@ ip6gre, ip6gretap, vti, - vti6, and - ip6tnl and accepts + vti6, + ip6tnl, and + erspan and accepts the following keys: Local= - A static local address for tunneled packets. It must - be an address on another interface of this host. + A static local address for tunneled packets. It must be an address on another interface of + this host, or the special value any. Remote= - The remote endpoint of the tunnel. + The remote endpoint of the tunnel. Takes an IP address or the special value + any. @@ -843,22 +957,24 @@ It is used as mark-configured SAD/SPD entry as part of the lookup key (both in data and control path) in ip xfrm (framework used to implement IPsec protocol). See - ip-xfrm — transform configuration for details. It is only used for VTI/VTI6 - tunnels. + ip-xfrm — transform configuration for details. It is only used for VTI/VTI6, + GRE, GRETAP, and ERSPAN tunnels. InputKey= The InputKey= parameter specifies the key to use for input. - The format is same as Key=. It is only used for VTI/VTI6 tunnels. + The format is same as Key=. It is only used for VTI/VTI6, GRE, GRETAP, + and ERSPAN tunnels. OutputKey= The OutputKey= parameter specifies the key to use for output. - The format is same as Key=. It is only used for VTI/VTI6 tunnels. + The format is same as Key=. It is only used for VTI/VTI6, GRE, GRETAP, + and ERSPAN tunnels. @@ -892,7 +1008,8 @@ FooOverUDP= Takes a boolean. Specifies whether FooOverUDP= tunnel is to be configured. - Defaults to false. For more detail information see + Defaults to false. This takes effects only for IPIP, SIT, GRE, and GRETAP tunnels. + For more detail information see Foo over UDP @@ -900,7 +1017,7 @@ FOUDestinationPort= This setting specifies the UDP destination port for encapsulation. - This field is mandatory and is not set by default. + This field is mandatory when FooOverUDP=yes, and is not set by default. @@ -913,7 +1030,7 @@ Encapsulation= - Accepts the same key as [FooOverUDP] + Accepts the same key as in the [FooOverUDP] section. @@ -934,8 +1051,8 @@ SerializeTunneledPackets= - 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. + Takes a boolean. If set to yes, then packets are serialized. Only applies for GRE, + GRETAP, and ERSPAN tunnels. When unset, the kernel's default will be used. @@ -958,13 +1075,6 @@ following keys: - - Protocol= - - The Protocol= 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. - - Encapsulation= @@ -987,7 +1097,17 @@ for delivery to the real destination. This option is mandatory. - + + Protocol= + + The Protocol= specifies the protocol number of the packets arriving + at the UDP port. When Encapsulation=FooOverUDP, this field is mandatory + and is not set by default. Takes an IP protocol name such as gre or + ipip, or an integer within the range 1-255. When + Encapsulation=GenericUDPEncapsulation, this must not be specified. + + + [Peer] Section Options @@ -1112,12 +1232,22 @@ The Base64 encoded private key for the interface. It can be generated using the wg genkey command (see wg8). - This option is mandatory to use WireGuard. + This option or PrivateKeyFile= 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 root:systemd-network with a 0640 file mode. + + PrivateKeyFile= + + Takes an absolute path to a file which contains the Base64 encoded private key for the interface. + When this option is specified, then PrivateKey= is ignored. + Note that the file must be readable by the user systemd-network, so it + should be, e.g., owned by root:systemd-network with a + 0640 file mode. + + ListenPort= @@ -1166,6 +1296,16 @@ with a 0640 file mode. + + PresharedKeyFile= + + Takes an absolute path to a file which contains the Base64 encoded preshared key for the + peer. When this option is specified, then PresharedKey= is ignored. + Note that the file must be readable by the user systemd-network, so it + should be, e.g., owned by root:systemd-network with a + 0640 file mode. + + AllowedIPs= diff --git a/man/systemd.network.xml b/man/systemd.network.xml index ee464ffff..412708470 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1,10 +1,7 @@ - - - + @@ -37,17 +34,17 @@ The main network file must have the extension .network; other extensions are ignored. Networks are applied to links whenever the links appear. - The .network files are read from the files located in the system - network directory /usr/lib/systemd/network, the volatile runtime network - directory /run/systemd/network and the local administration network - directory /etc/systemd/network. All configuration files are collectively - sorted and processed in lexical order, regardless of the directories in which they live. - However, files with identical filenames replace each other. Files in /etc - have the highest priority, files in /run take precedence over files with - the same name in /usr/lib. This can be used to override a system-supplied - configuration file with a local file if needed. As a special case, an empty file (file size 0) - or symlink with the same name pointing to /dev/null disables the - configuration file entirely (it is "masked"). + The .network files are read from the files located in the system network + directories /usr/lib/systemd/network and + /usr/local/lib/systemd/network, the volatile runtime network directory + /run/systemd/network and the local administration network directory + /etc/systemd/network. All configuration files are collectively sorted and processed + in lexical order, regardless of the directories in which they live. However, files with identical + filenames replace each other. Files in /etc have the highest priority, files in + /run take precedence over files with the same name under + /usr. This can be used to override a system-supplied configuration file with a local + file if needed. As a special case, an empty file (file size 0) or symlink with the same name pointing to + /dev/null disables the configuration file entirely (it is "masked"). Along with the network file foo.network, a "drop-in" directory foo.network.d/ may exist. All files with the suffix @@ -60,9 +57,7 @@ /run/systemd/network directories. Drop-in files in /etc take precedence over those in /run which in turn take precedence over those in /usr/lib. Drop-in files under any of these - directories take precedence over the main netdev file wherever located. (Of course, since - /run is temporary and /usr/lib is for vendors, it is - unlikely drop-ins should be used in either of those places.) + directories take precedence over the main netdev file wherever located. Note that an interface without any static IPv6 addresses configured, and neither DHCPv6 nor IPv6LL enabled, shall be considered to have no IPv6 support. IPv6 will be automatically @@ -142,53 +137,55 @@ Host= - Matches against the hostname or machine ID of the - host. See ConditionHost= in + Matches against the hostname or machine ID of the host. See + ConditionHost= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. Virtualization= - Checks whether the system is executed in a virtualized - environment and optionally test whether it is a specific - implementation. See ConditionVirtualization= in + Checks whether the system is executed in a virtualized environment and optionally test + whether it is a specific implementation. See ConditionVirtualization= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. KernelCommandLine= - Checks whether a specific kernel command line option is - set (or if prefixed with the exclamation mark unset). See + Checks whether a specific kernel command line option is set. See ConditionKernelCommandLine= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. KernelVersion= - Checks whether the kernel version (as reported by uname -r) matches a certain - expression (or if prefixed with the exclamation mark does not match it). See - ConditionKernelVersion= in - systemd.unit5 for - details. + Checks whether the kernel version (as reported by uname -r) matches a + certain expression. See ConditionKernelVersion= in + systemd.unit5 + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. Architecture= - Checks whether the system is running on a specific - architecture. See ConditionArchitecture= in + Checks whether the system is running on a specific architecture. See + ConditionArchitecture= in systemd.unit5 - for details. + for details. When prefixed with an exclamation mark (!), the result is negated. + If an empty string is assigned, then previously assigned value is cleared. @@ -257,15 +254,19 @@ RequiredForOnline= - Takes a boolean. When yes, the network is deemed - required when determining whether the system is online when running - systemd-networkd-wait-online. - When no, the network is ignored when checking for - online state. Defaults to yes. + Takes a boolean or operational state. Please see + networkctl1 + for possible operational states. When yes, the network is deemed required when + determining whether the system is online when running + systemd-networkd-wait-online. When no, the network is ignored + when checking for online state. When an operational state is set, yes is implied, + and this controls the operational state required for the network interface to be considered online. + Defaults to yes. + The network will be brought up normally in all cases, but in 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 systemd-networkd-wait-online + skipped automatically by systemd-networkd-wait-online if RequiredForOnline=no. @@ -311,7 +312,7 @@ DHCPServer= - Takes a boolean. If set to yes, DHCPv4 server will be start. Defaults + Takes a boolean. If set to yes, DHCPv4 server will be started. Defaults to no. Further settings for the DHCP server may be set in the [DHCPServer] section described below. @@ -320,10 +321,10 @@ LinkLocalAddressing= - Enables link-local address autoconfiguration. Accepts - yes, no, - ipv4, or ipv6. Defaults to - ipv6. + Enables link-local address autoconfiguration. Accepts yes, + no, ipv4, or ipv6. If + Bridge= is set, defaults to no, and if not, + defaults to ipv6. @@ -481,18 +482,14 @@ specified more than once. - If the specified address is 0.0.0.0 (for IPv4) or - [::] (for IPv6), a new address range of the requested size - is automatically allocated from a system-wide pool of - unused ranges. The allocated range is checked against all - current network interfaces and all known network - configuration files to avoid address range conflicts. The - default system-wide pool consists of 192.168.0.0/16, - 172.16.0.0/12 and 10.0.0.0/8 for IPv4, and fc00::/7 for - IPv6. This functionality is useful to manage a large - number of dynamically created network interfaces with the - same network configuration and automatic address range - assignment. + If the specified address is 0.0.0.0 (for IPv4) or :: + (for IPv6), a new address range of the requested size is automatically allocated from a + system-wide pool of unused ranges. Note that the prefix length must be equal or larger than 8 for + IPv4, and 64 for IPv6. The allocated range is checked against all current network interfaces and + all known network configuration files to avoid address range conflicts. The default system-wide + pool consists of 192.168.0.0/16, 172.16.0.0/12 and 10.0.0.0/8 for IPv4, and fd00::/8 for IPv6. + This functionality is useful to manage a large number of dynamically created network interfaces + with the same network configuration and automatic address range assignment. @@ -632,6 +629,11 @@ url="https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt">ip-sysctl.txt in the kernel documentation regarding accept_ra, but note that systemd's setting of 1 (i.e. true) corresponds to kernel's setting of 2. + + Note that if this option is enabled a userspace implementation of the IPv6 RA protocol is + used, and the kernel's own implementation remains disabled, since `networkd` needs to know all + details supplied in the advertisements, and these are not available from the kernel if the kernel's + own implemenation is used. @@ -800,6 +802,15 @@ + + IgnoreCarrierLoss= + + A boolean. Allows networkd to retain both the static and dynamic configuration of the + interface even if its carrier is lost. Defaults to false. + + + + @@ -815,15 +826,15 @@ Address= - As in the [Network] section. This - key is mandatory. + As in the [Network] section. This key is mandatory. Each + [Address] section can contain one Address= setting. Peer= The peer address in a point-to-point connection. - Accepts the same format as the Address + Accepts the same format as the Address= key. @@ -834,7 +845,7 @@ described in inet_pton3. This key only applies to IPv4 addresses. If it is not - given, it is derived from the Address + given, it is derived from the Address= key. @@ -1074,7 +1085,7 @@ - GatewayOnlink= + GatewayOnLink= 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 @@ -1256,6 +1267,11 @@ UseRoutes=, SendHostname=, UseMTU=, VendorClassIdentifier=, UseTimezone=. + + With this option enabled DHCP requests will mimic those generated by Microsoft Windows, in + order to reduce the ability to fingerprint and recognize installations. This means DHCP request + sizes will grow and lease data will be more comprehensive than normally, though most of the + requested data is not actually used. @@ -1504,6 +1520,23 @@ + + + UseAutonomousPrefix= + + When true (the default), the autonomous prefix received in the Router Advertisement will be used and take + precedence over any statically configured ones. + + + + + UseOnLinkPrefix= + + When true (the default), the onlink prefix received in the Router Advertisement will be used and take + precedence over any statically configured ones. + + + @@ -1765,6 +1798,15 @@ + + MulticastFlood= + + Takes a boolean. Controls whether the bridge should flood + traffic for which an MDB entry is missing and the destination + is unknown through this port. When unset, the kernel's default will be used. + + + MulticastToUnicast= @@ -1774,6 +1816,22 @@ + + NeighborSuppression= + + Takes a boolean. Configures whether ARP and ND neighbor suppression is enabled for + this port. When unset, the kernel's default will be used. + + + + + Learning= + + Takes a boolean. Configures whether MAC address learning is enabled for + this port. When unset, the kernel's default will be used. + + + HairPin= @@ -1883,6 +1941,13 @@ automatic restart off. By default automatic restart is disabled. + + TripleSampling= + + Takes a boolean. When yes, three samples (instead of one) are used to determine + the value of a received bit by majority rule. When unset, the kernel's default will be used. + + diff --git a/man/systemd.nspawn.xml b/man/systemd.nspawn.xml index 7924641df..1485a26f0 100644 --- a/man/systemd.nspawn.xml +++ b/man/systemd.nspawn.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + @@ -428,6 +425,17 @@ is privileged (see above). + + Inaccessible= + + Masks the specified file or directly in the container, by over-mounting it with an empty file + node of the same type with the most restrictive access mode. Takes a file system path as arugment. This option + may be used multiple times to mask multiple files or directories. This option is equivalent to the command line + switch , see + systemd-nspawn1 for details + about the specific options supported. This setting is privileged (see above). + + Overlay= OverlayReadOnly= diff --git a/man/systemd.offline-updates.xml b/man/systemd.offline-updates.xml index 13fdfc28d..cd9c1b586 100644 --- a/man/systemd.offline-updates.xml +++ b/man/systemd.offline-updates.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.path.xml b/man/systemd.path.xml index e513fe6d4..de284d877 100644 --- a/man/systemd.path.xml +++ b/man/systemd.path.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.preset.xml b/man/systemd.preset.xml index cf807bd4c..30c838b3d 100644 --- a/man/systemd.preset.xml +++ b/man/systemd.preset.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/systemd.resource-control.xml b/man/systemd.resource-control.xml index a4d793c32..4a8c57f45 100644 --- a/man/systemd.resource-control.xml +++ b/man/systemd.resource-control.xml @@ -1,10 +1,7 @@ - - - + + @@ -198,6 +195,25 @@ + + CPUQuotaPeriodSec= + + + Assign the duration over which the CPU time quota specified by CPUQuota= is measured. + Takes a time duration value in seconds, with an optional suffix such as "ms" for milliseconds (or "s" for seconds.) + The default setting is 100ms. The period is clamped to the range supported by the kernel, which is [1ms, 1000ms]. + Additionally, the period is adjusted up so that the quota interval is also at least 1ms. + Setting CPUQuotaPeriodSec= to an empty value resets it to the default. + + This controls the second field of cpu.max attribute on the unified control group hierarchy + and cpu.cfs_period_us on legacy. For details about these control group attributes, see + cgroup-v2.txt and + sched-design-CFS.txt. + + Example: CPUQuotaPeriodSec=10ms to request that the CPU quota is measured in periods of 10ms. + + + MemoryAccounting= @@ -497,23 +513,27 @@ IPAddressDeny=ADDRESS[/PREFIXLENGTH]… - Turn on address range network traffic filtering for packets sent and received over AF_INET and AF_INET6 - sockets. Both directives take a space separated list of IPv4 or IPv6 addresses, each optionally suffixed - with an address prefix length (separated by a / character). If the latter is omitted, the - address is considered a host address, i.e. the prefix covers the whole address (32 for IPv4, 128 for IPv6). - + Turn on address range network traffic filtering for IP packets sent and received over + AF_INET and AF_INET6 sockets. Both directives take a + space separated list of IPv4 or IPv6 addresses, each optionally suffixed with an address prefix + length in bits (separated by a / character). If the latter is omitted, the + address is considered a host address, i.e. the prefix covers the whole address (32 for IPv4, 128 + for IPv6). - The access lists configured with this option are applied to all sockets created by processes of this - unit (or in the case of socket units, associated with it). The lists are implicitly combined with any lists - configured for any of the parent slice units this unit might be a member of. By default all access lists are - empty. When configured the lists are enforced as follows: + The access lists configured with this option are applied to all sockets created by processes + of this unit (or in the case of socket units, associated with it). The lists are implicitly + combined with any lists configured for any of the parent slice units this unit might be a member + of. By default all access lists are empty. Both ingress and egress traffic is filtered by these + settings. In case of ingress traffic the source IP address is checked against these access lists, + in case of egress traffic the destination IP address is checked. When configured the lists are + enforced as follows: - Access will be granted in case its destination/source address matches any entry in the - IPAddressAllow= setting. + Access will be granted in case an IP packet's destination/source address matches + any entry in the IPAddressAllow= setting. - Otherwise, access will be denied in case its destination/source address matches any entry - in the IPAddressDeny= setting. + Otherwise, access will be denied in case its destination/source address matches + any entry in the IPAddressDeny= setting. Otherwise, access will be granted. diff --git a/man/systemd.scope.xml b/man/systemd.scope.xml index 18560fb7e..503a480dd 100644 --- a/man/systemd.scope.xml +++ b/man/systemd.scope.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/systemd.service.xml b/man/systemd.service.xml index f0f9aee15..5b8841753 100644 --- a/man/systemd.service.xml +++ b/man/systemd.service.xml @@ -1,10 +1,7 @@ - - - + @@ -62,6 +59,10 @@ about the incompatibilities, see the Incompatibilities with SysV document. + + The systemd-run1 + command allows creating .service and .scope units dynamically + and transiently from the command line. @@ -338,6 +339,11 @@ If the executable path is prefixed with -, 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. + + : + If the executable path is prefixed with :, environment variable substitution (as described by the "Command Lines" section below) is not applied. + + + If the executable path is prefixed with + then the process is executed with full privileges. In this mode privilege restrictions configured with User=, Group=, CapabilityBoundingSet= or the various file system namespacing options (such as PrivateDevices=, PrivateTmp=) are not applied to the invoked command line (but still affect any other ExecStart=, ExecStop=, … lines). @@ -358,7 +364,7 @@ - @, -, and one of + @, -, :, and one of +/!/!! may be used together and they can appear in any order. However, only one of +, !, !! may be used at a time. Note that these prefixes are also supported for the other command line settings, @@ -471,14 +477,18 @@ start-up failed, for example because any of the commands specified in ExecStart=, ExecStartPre= or ExecStartPost= failed (and weren't prefixed with -, see above) or timed out. Use ExecStopPost= to invoke commands when a - service failed to start up correctly and is shut down again. Also note that, service restart requests are - implemented as stop operations followed by start operations. This means that ExecStop= and - ExecStopPost= are executed during a service restart operation. + service failed to start up correctly and is shut down again. Also note that the stop operation is always + performed if the service started successfully, even if the processes in the service terminated on their + own or were killed. The stop commands must be prepared to deal with that case. $MAINPID + will be unset if systemd knows that the main process exited by the time the stop commands are called. - It is recommended to use this setting for commands that communicate with the service requesting clean - termination. When the commands specified with this option are executed it should be assumed that the service is - still fully up and is able to react correctly to all commands. For post-mortem clean-up steps use - ExecStopPost= instead. + Service restart requests are implemented as stop operations followed by start operations. This + means that ExecStop= and ExecStopPost= are executed during a + service restart operation. + + It is recommended to use this setting for commands that communicate with the service requesting + clean termination. For post-mortem clean-up steps use ExecStopPost= instead. + @@ -802,24 +812,26 @@ RestartPreventExitStatus= - Takes a list of exit status definitions that, - when returned by the main service process, will prevent - automatic service restarts, regardless of the restart setting - configured with Restart=. Exit status - definitions can either be numeric exit codes or termination - signal names, and are separated by spaces. Defaults to the - empty list, so that, by default, no exit status is excluded - from the configured restart logic. For example: + + Takes a list of exit status definitions that, when returned by the main service + process, will prevent automatic service restarts, regardless of the restart setting configured with + Restart=. Exit status definitions can either be numeric exit codes or termination + signal names, and are separated by spaces. Defaults to the empty list, so that, by default, no exit + status is excluded from the configured restart logic. For example: RestartPreventExitStatus=1 6 SIGABRT - ensures that exit codes 1 and 6 and the termination signal - SIGABRT will not result in automatic - service restarting. This option may appear more than once, in - which case the list of restart-preventing statuses is - merged. If the empty string is assigned to this option, the - list is reset and all prior assignments of this option will - have no effect. + ensures that exit codes 1 and 6 and the termination signal SIGABRT will not + result in automatic service restarting. This option may appear more than once, in which case the list + of restart-preventing statuses is merged. If the empty string is assigned to this option, the list is + reset and all prior assignments of this option will have no effect. + + Note that this setting has no effect on processes configured via + ExecStartPre=, ExecStartPost=, ExecStop=, + ExecStopPost= or ExecReload=, but only on the main service + process, i.e. either the one invoked by ExecStart= or (depending on + Type=, PIDFile=, …) the otherwise configured main + process. @@ -1421,7 +1433,8 @@ WantedBy=multi-user.target systemd.exec5, systemd.resource-control5, systemd.kill5, - systemd.directives7 + systemd.directives7, + systemd-run1 diff --git a/man/systemd.slice.xml b/man/systemd.slice.xml index 43e7c6a73..5019bf997 100644 --- a/man/systemd.slice.xml +++ b/man/systemd.slice.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/systemd.socket.xml b/man/systemd.socket.xml index 7547071e9..e29602b06 100644 --- a/man/systemd.socket.xml +++ b/man/systemd.socket.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.special.xml b/man/systemd.special.xml index fd5639ba0..248fb924d 100644 --- a/man/systemd.special.xml +++ b/man/systemd.special.xml @@ -1,10 +1,7 @@ - - - + @@ -80,9 +77,11 @@ sysinit.target, system-update.target, system-update-pre.target, + time-set.target, time-sync.target, timers.target, umount.target, + usb-gadget.target, -.slice, system.slice, user.slice, @@ -799,6 +798,16 @@ dynamically when audio hardware is found. + + usb-gadget.target + + This target is started automatically as soon as a + USB Device Controller becomes available at boot. + + This may be used to pull in usb gadget + dynamically when UDC hardware is found. + + @@ -942,6 +951,18 @@ the $portmap facility. + + time-set.target + + Services responsible for setting the system clock from + a local source (such as a maintained timestamp file or + imprecise real-time clock) should pull in this target and + order themselves before it. Services where approximate time + is desired should be ordered after this unit, but not pull + it in. This target does not provide the accuracy guarantees + of time-sync.target. + + time-sync.target @@ -1014,10 +1035,23 @@ Special User Units When systemd runs as a user instance, the following special - units are available, which have similar definitions as their + units are available: + + + + default.target + + This is the main target of the user session, started by default. Various services that + compose the normal user session should be pulled into this target. In this regard, + default.target is similar to multi-user.target in the + system instance, but it is a real unit, not an alias. + + + + + In addition, the following units are available which have definitions similar to their system counterparts: exit.target, - default.target, shutdown.target, sockets.target, timers.target, diff --git a/man/systemd.swap.xml b/man/systemd.swap.xml index cf0045150..66d63503d 100644 --- a/man/systemd.swap.xml +++ b/man/systemd.swap.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.syntax.xml b/man/systemd.syntax.xml index 67cd640ee..3b58e4528 100644 --- a/man/systemd.syntax.xml +++ b/man/systemd.syntax.xml @@ -1,10 +1,9 @@ - %entities; ]> - @@ -66,7 +65,7 @@ Each file is a plain text file divided into sections, with configuration entries in the style key=value. - Empty lines and lines starting with # or ; are + Whitespace immediately before or after the = is ignored. Empty lines and lines starting with # or ; are ignored, which may be used for commenting. Lines ending in a backslash are concatenated with the following line while reading and the diff --git a/man/systemd.target.xml b/man/systemd.target.xml index 10aa91a2e..a706a4588 100644 --- a/man/systemd.target.xml +++ b/man/systemd.target.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.time.xml b/man/systemd.time.xml index 24df5ab94..4a6b808c0 100644 --- a/man/systemd.time.xml +++ b/man/systemd.time.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/systemd.timer.xml b/man/systemd.timer.xml index d254c776b..6a13e52cc 100644 --- a/man/systemd.timer.xml +++ b/man/systemd.timer.xml @@ -1,10 +1,7 @@ - - - + @@ -108,23 +105,49 @@ OnUnitInactiveSec= Defines monotonic timers relative to different - starting points: OnActiveSec= defines a - timer relative to the moment the timer itself is activated. - OnBootSec= defines a timer relative to when - the machine was booted up. OnStartupSec= - defines a timer relative to when systemd was first started. - OnUnitActiveSec= defines a timer relative - to when the unit the timer is activating was last activated. - OnUnitInactiveSec= defines a timer relative - to when the unit the timer is activating was last - deactivated. + starting points: - Multiple directives may be combined of the same and of - different types. For example, by combining - OnBootSec= and - OnUnitActiveSec=, it is possible to define - a timer that elapses in regular intervals and activates a - specific service each time. + + Settings and their starting points + + + + + Setting + Meaning + + + + + OnActiveSec= + Defines a timer relative to the moment the timer unit itself is activated. + + + OnBootSec= + Defines a timer relative to when the machine was booted up. In containers, for the system manager instance, this is mapped to OnStartupSec=, making both equivalent. + + + OnStartupSec= + Defines a timer relative to when the service manager was first started. For system timer units this is very similar to OnBootSec= as the system service manager is generally started very early at boot. It's primarily useful when configured in units running in the per-user service manager, as the user service manager is generally started on first login only, not already during boot. + + + OnUnitActiveSec= + Defines a timer relative to when the unit the timer unit is activating was last activated. + + + OnUnitInactiveSec= + Defines a timer relative to when the unit the timer unit is activating was last deactivated. + + + +
+ + Multiple directives may be combined of the same and of different types, in which case the timer + unit will trigger whenever any of the specified timer expressions elapse. For example, by combining + OnBootSec= and OnUnitActiveSec=, it is possible to define a + timer that elapses in regular intervals and activates a specific service each time. Moreover, both + monotonic time expressions and OnCalendar= calendar expressions may be combined in + the same timer unit. The arguments to the directives are time spans configured in seconds. Example: "OnBootSec=50" means 50s after @@ -139,13 +162,12 @@ and the configured unit is started. This is not the case for timers defined in the other directives. - These are monotonic timers, independent of wall-clock - time and timezones. If the computer is temporarily suspended, - the monotonic clock stops too. + These are monotonic timers, independent of wall-clock time and timezones. If the computer is + temporarily suspended, the monotonic clock pauses, too. - If the empty string is assigned to any of these options, - the list of timers is reset, and all prior assignments will - have no effect. + If the empty string is assigned to any of these options, the list of timers is reset (both + monotonic timers and OnCalendar= timers, see below), and all prior assignments + will have no effect. Note that timers do not necessarily expire at the precise time configured with these settings, as they are @@ -169,7 +191,13 @@ the AccuracySec= setting below. - May be specified more than once.
+ May be specified more than once, in which case the timer unit will trigger whenever any of the + specified expressions elapse. Moreover calendar timers and monotonic timers (see above) may be + combined within the same timer unit. + + If the empty string is assigned to any of these options, the list of timers is reset (both + OnCalendar= timers and monotonic timers, see above), and all prior assignments + will have no effect.
@@ -231,6 +259,17 @@ AccuracySec=1us. + + OnClockChange= + OnTimezoneChange= + + These options take boolean arguments. When true, the service unit will be triggered + when the system clock (CLOCK_REALTIME) jumps relative to the monotonic clock + (CLOCK_MONOTONIC), or when the local system timezone is modified. These options + can be used alone or in combination with other timer expressions (see above) within the same timer + unit. These options default to false. + + Unit= diff --git a/man/systemd.unit.xml b/man/systemd.unit.xml index f21f9eae4..81a02253e 100644 --- a/man/systemd.unit.xml +++ b/man/systemd.unit.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + @@ -329,7 +326,7 @@ /etc/systemd/system - Local configuration + System units created by the administrator /run/systemd/system @@ -342,10 +339,11 @@ /usr/local/lib/systemd/system - Units of installed packages + System units installed by the administrator /usr/lib/systemd/system + System units installed by the distribution package manager /run/systemd/generator.late @@ -394,7 +392,7 @@ /etc/systemd/user - Local configuration + User units created by the administrator $XDG_RUNTIME_DIR/systemd/user @@ -419,10 +417,11 @@ /usr/local/lib/systemd/user - Units of packages that have been installed system-wide + User units installed by the administrator /usr/lib/systemd/user + User units installed by the distribution package manager $XDG_RUNTIME_DIR/systemd/generator.late @@ -728,23 +727,18 @@ JoinsNamespaceOf= - For units that start processes (such as - service units), lists one or more other units whose network - and/or temporary file namespace to join. This only applies to - unit types which support the - PrivateNetwork= and + For units that start processes (such as service units), lists one or more other units + whose network and/or temporary file namespace to join. This only applies to unit types which support + the PrivateNetwork=, NetworkNamespacePath= and PrivateTmp= directives (see - systemd.exec5 - for details). If a unit that has this setting set is started, - its processes will see the same /tmp, - /var/tmp and network namespace as one - listed unit that is started. If multiple listed units are - already started, it is not defined which namespace is joined. - Note that this setting only has an effect if - PrivateNetwork= and/or - PrivateTmp= is enabled for both the unit - that joins the namespace and the unit whose namespace is - joined. + systemd.exec5 for + details). If a unit that has this setting set is started, its processes will see the same + /tmp, /var/tmp and network namespace as one listed unit + that is started. If multiple listed units are already started, it is not defined which namespace is + joined. Note that this setting only has an effect if + PrivateNetwork=/NetworkNamespacePath= and/or + PrivateTmp= is enabled for both the unit that joins the namespace and the unit + whose namespace is joined. @@ -1010,6 +1004,8 @@ ConditionUser= ConditionGroup= ConditionControlGroupController= + ConditionMemory= + ConditionCPUs= + diff --git a/man/sysusers.d.xml b/man/sysusers.d.xml index e47d36c73..4314732c6 100644 --- a/man/sysusers.d.xml +++ b/man/sysusers.d.xml @@ -1,9 +1,8 @@ - - + + diff --git a/man/telinit.xml b/man/telinit.xml index 5a48caff2..43871eb35 100644 --- a/man/telinit.xml +++ b/man/telinit.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/threads-aware.xml b/man/threads-aware.xml index 7985f4acd..49de3444b 100644 --- a/man/threads-aware.xml +++ b/man/threads-aware.xml @@ -1,9 +1,7 @@ - - - + + diff --git a/man/timedatectl.xml b/man/timedatectl.xml index b75b4cc72..b4b99103d 100644 --- a/man/timedatectl.xml +++ b/man/timedatectl.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/timesyncd.conf.xml b/man/timesyncd.conf.xml index 8ba639f3a..c64d01540 100644 --- a/man/timesyncd.conf.xml +++ b/man/timesyncd.conf.xml @@ -1,10 +1,7 @@ - - - + diff --git a/man/tmpfiles.d.xml b/man/tmpfiles.d.xml index 3f2ef7ee9..67bd1dc72 100644 --- a/man/tmpfiles.d.xml +++ b/man/tmpfiles.d.xml @@ -1,5 +1,6 @@ - + + diff --git a/man/udev.xml b/man/udev.xml index 74aab8e02..5a78be320 100644 --- a/man/udev.xml +++ b/man/udev.xml @@ -1,5 +1,5 @@ - - %entities; ]> - - + diff --git a/man/udev_device_has_tag.xml b/man/udev_device_has_tag.xml index e27b77021..9c64a4b45 100644 --- a/man/udev_device_has_tag.xml +++ b/man/udev_device_has_tag.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_device_new_from_syspath.xml b/man/udev_device_new_from_syspath.xml index 8e207b4aa..118adfa2c 100644 --- a/man/udev_device_new_from_syspath.xml +++ b/man/udev_device_new_from_syspath.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_enumerate_add_match_subsystem.xml b/man/udev_enumerate_add_match_subsystem.xml index a45ca5d2d..0dbb926ff 100644 --- a/man/udev_enumerate_add_match_subsystem.xml +++ b/man/udev_enumerate_add_match_subsystem.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_enumerate_new.xml b/man/udev_enumerate_new.xml index 3ece35eab..013d5cc82 100644 --- a/man/udev_enumerate_new.xml +++ b/man/udev_enumerate_new.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_enumerate_scan_devices.xml b/man/udev_enumerate_scan_devices.xml index 770ddb946..d3dddd415 100644 --- a/man/udev_enumerate_scan_devices.xml +++ b/man/udev_enumerate_scan_devices.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_list_entry.xml b/man/udev_list_entry.xml index 0b1d28a0e..9f44812cb 100644 --- a/man/udev_list_entry.xml +++ b/man/udev_list_entry.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_monitor_filter_update.xml b/man/udev_monitor_filter_update.xml index f5e4d075c..88a19f1da 100644 --- a/man/udev_monitor_filter_update.xml +++ b/man/udev_monitor_filter_update.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_monitor_new_from_netlink.xml b/man/udev_monitor_new_from_netlink.xml index 47fa5ab49..cc37d2193 100644 --- a/man/udev_monitor_new_from_netlink.xml +++ b/man/udev_monitor_new_from_netlink.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_monitor_receive_device.xml b/man/udev_monitor_receive_device.xml index 6a4aae90f..5b3f097d1 100644 --- a/man/udev_monitor_receive_device.xml +++ b/man/udev_monitor_receive_device.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udev_new.xml b/man/udev_new.xml index 3971acbc7..89fa05e38 100644 --- a/man/udev_new.xml +++ b/man/udev_new.xml @@ -1,13 +1,10 @@ - %entities; ]> - - + diff --git a/man/udevadm.xml b/man/udevadm.xml index b7a2494f8..467402ca7 100644 --- a/man/udevadm.xml +++ b/man/udevadm.xml @@ -1,10 +1,7 @@ - - - + @@ -189,7 +186,7 @@ Request device events from the kernel. Primarily used to replay events at system coldplug time. - Takes a device specification as a positional argument. See the description of info + Takes device specifications as positional arguments. See the description of info above. @@ -220,8 +217,10 @@ - Type of event to be triggered. The default value is - change. + Type of event to be triggered. Possible actions are add, + remove, change, move, + online, offline, bind, + and unbind. The default value is change. @@ -298,7 +297,7 @@ Trigger events for devices with a matching device path. When this option is specified more than once, - the last NAME is used. + then each matching result is ORed, that is, all specified devices are triggered. @@ -306,7 +305,7 @@ Trigger events for all children of a given device. When this option is specified more than once, - the last NAME is used. + then each matching result is ORed, that is, all children of each specified device are triggered. @@ -332,8 +331,8 @@ - In addition, an optional positional argument can be used - to specify device name or sys path. It must start with + In addition, optional positional arguments can be used + to specify device names or sys paths. They must start with /dev or /sys respectively. @@ -372,8 +371,10 @@ - Signal and wait for systemd-udevd to exit. Note that systemd-udevd.service - contains and so as a result, this option restarts systemd-udevd. + Signal and wait for systemd-udevd to exit. No option except for + can be specified after this option. + Note that systemd-udevd.service contains + and so as a result, this option restarts systemd-udevd. If you want to stop systemd-udevd.service, please use the following: systemctl stop systemd-udevd-control.socket systemd-udevd-kernel.socket systemd-udevd.service diff --git a/man/user-system-options.xml b/man/user-system-options.xml index 4ad2d3b16..195c2e416 100644 --- a/man/user-system-options.xml +++ b/man/user-system-options.xml @@ -1,10 +1,7 @@ - - - + + diff --git a/man/user@.service.xml b/man/user@.service.xml index 30af3c8bf..e748c03b8 100644 --- a/man/user@.service.xml +++ b/man/user@.service.xml @@ -1,5 +1,6 @@ - + diff --git a/man/vconsole.conf.xml b/man/vconsole.conf.xml index a0ca83507..2e2223cc6 100644 --- a/man/vconsole.conf.xml +++ b/man/vconsole.conf.xml @@ -1,10 +1,7 @@ - - - + diff --git a/meson.build b/meson.build index 56c98b9c1..79195c974 100644 --- a/meson.build +++ b/meson.build @@ -1,7 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1+ project('systemd', 'c', - version : '241', + version : '242', license : 'LGPLv2+', default_options: [ 'c_std=gnu99', @@ -12,8 +12,8 @@ project('systemd', 'c', meson_version : '>= 0.46', ) -libsystemd_version = '0.25.0' -libudev_version = '1.6.13' +libsystemd_version = '0.26.0' +libudev_version = '1.6.14' # We need the same data in two different formats, ugh! # Also, for hysterical reasons, we use different variable @@ -26,6 +26,10 @@ substs = configuration_data() substs.set('PROJECT_URL', 'https://www.freedesktop.org/wiki/Software/systemd') substs.set('PROJECT_VERSION', meson.project_version()) +# This is to be used instead of meson.source_root(), as the latter will return +# the wrong result when systemd is being built as a meson subproject +project_source_root = meson.current_source_dir() + want_ossfuzz = get_option('oss-fuzz') want_libfuzzer = get_option('llvm-fuzz') if want_ossfuzz and want_libfuzzer @@ -36,7 +40,7 @@ fuzzer_build = want_ossfuzz or want_libfuzzer ##################################################################### # Try to install the git pre-commit hook -git_hook = run_command(join_paths(meson.source_root(), 'tools/add-git-hook.sh')) +git_hook = run_command(join_paths(project_source_root, 'tools/add-git-hook.sh')) if git_hook.returncode() == 0 message(git_hook.stdout().strip()) endif @@ -66,6 +70,7 @@ rootprefix_default = split_usr ? '/' : '/usr' if rootprefixdir == '' rootprefixdir = rootprefix_default endif +rootprefixdir_noslash = rootprefixdir == '/' ? '' : rootprefixdir sysvinit_path = get_option('sysvinit-path') sysvrcnd_path = get_option('sysvrcnd-path') @@ -76,7 +81,7 @@ 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 +# join_paths ignores the preceding arguments if an absolute component is # encountered, so this should canonicalize various paths when they are # absolute or relative. prefixdir = get_option('prefix') @@ -135,7 +140,6 @@ systemsleepdir = join_paths(rootlibexecdir, 'system-sleep') systemunitdir = join_paths(rootprefixdir, 'lib/systemd/system') systempresetdir = join_paths(rootprefixdir, 'lib/systemd/system-preset') udevlibexecdir = join_paths(rootprefixdir, 'lib/udev') -udevhomedir = udevlibexecdir udevrulesdir = join_paths(udevlibexecdir, 'rules.d') udevhwdbdir = join_paths(udevlibexecdir, 'hwdb.d') catalogdir = join_paths(prefixdir, 'lib/systemd/catalog') @@ -235,11 +239,13 @@ conf.set10('MEMORY_ACCOUNTING_DEFAULT', memory_accounting_ conf.set_quoted('MEMORY_ACCOUNTING_DEFAULT_YES_NO', memory_accounting_default ? 'yes' : 'no') substs.set('prefix', prefixdir) +substs.set('rootprefix', rootprefixdir) +substs.set('rootprefix_noslash', rootprefixdir_noslash) substs.set('exec_prefix', prefixdir) substs.set('libdir', libdir) substs.set('rootlibdir', rootlibdir) substs.set('includedir', includedir) -substs.set('pkgsysconfdir', pkgsysconfdir) +substs.set('sysconfdir', sysconfdir) substs.set('bindir', bindir) substs.set('rootbindir', rootbindir) substs.set('rootlibexecdir', rootlibexecdir) @@ -334,6 +340,8 @@ possible_cc_flags = [ '-Wno-missing-field-initializers', '-Wno-unused-result', '-Wno-format-signedness', + '-Wno-error=#warnings', # clang + '-Wno-string-plus-int', # clang # work-around for gcc 7.1 turning this on on its own. '-Wno-error=nonnull', @@ -375,6 +383,16 @@ if get_option('buildtype') != 'debug' possible_link_flags += '-Wl,--gc-sections' endif +if get_option('b_ndebug') == 'true' + # With asserts disabled with get a bunch of warnings about variables which + # are used only in the asserts. This is not useful at all, so let's just silence + # those warnings. + possible_cc_flags += [ + '-Wno-unused-variable', + '-Wno-unused-but-set-variable', + ] +endif + add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'c') add_project_link_arguments(cc.get_supported_link_arguments(possible_link_flags), language : 'c') @@ -409,15 +427,11 @@ conf.set('SIZEOF_RLIM_T', cc.sizeof('rlim_t', prefix : '#include -#include -#include #include ''' foreach decl : ['char16_t', 'char32_t', - 'struct fib_rule_uid_range', - 'struct fib_rule_port_range', 'struct statx', ] @@ -575,8 +589,8 @@ endif ##################################################################### -vcs_tagger = [meson.source_root() + '/tools/meson-vcs-tag.sh', - meson.source_root(), +vcs_tagger = [project_source_root + '/tools/meson-vcs-tag.sh', + project_source_root, get_option('version-tag'), meson.project_version()] @@ -595,7 +609,7 @@ git = find_program('git', required : false) env = find_program('env') perl = find_program('perl', required : false) -meson_make_symlink = meson.source_root() + '/tools/meson-make-symlink.sh' +meson_make_symlink = project_source_root + '/tools/meson-make-symlink.sh' mkdir_p = 'mkdir -p $DESTDIR/@0@' test_efi_create_disk_sh = find_program('test/test-efi-create-disk.sh') splash_bmp = files('test/splash.bmp') @@ -818,7 +832,9 @@ conf.set10('ENABLE_WHEEL_GROUP', get_option('wheel-group')) 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')) +group_render_mode = get_option('group-render-mode') +substs.set('GROUP_RENDER_MODE', group_render_mode) +conf.set10('GROUP_RENDER_UACCESS', group_render_mode != '0666') kill_user_processes = get_option('default-kill-user-processes') conf.set10('KILL_USER_PROCESSES', kill_user_processes) @@ -1444,6 +1460,7 @@ includes = include_directories('src/basic', 'src/udev', 'src/libudev', 'src/core', + 'src/shutdown', 'src/libsystemd/sd-bus', 'src/libsystemd/sd-device', 'src/libsystemd/sd-event', @@ -1475,7 +1492,7 @@ libjournal_core = static_library( include_directories : includes, install : false) -libsystemd_sym_path = '@0@/@1@'.format(meson.current_source_dir(), libsystemd_sym) +libsystemd_sym_path = '@0@/@1@'.format(project_source_root, libsystemd_sym) libsystemd = shared_library( 'systemd', disable_mempool_c, @@ -1530,6 +1547,7 @@ public_programs = [] subdir('src/libudev') subdir('src/shared') subdir('src/core') +subdir('src/shutdown') subdir('src/udev') subdir('src/network') @@ -1577,7 +1595,7 @@ foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'], module = tuple[0] sym = 'src/nss-@0@/nss-@0@.sym'.format(module) - version_script_arg = join_paths(meson.source_root(), sym) + version_script_arg = join_paths(project_source_root, sym) nss = shared_library( 'nss_' + module, @@ -1588,8 +1606,7 @@ foreach tuple : [['myhostname', 'ENABLE_NSS_MYHOSTNAME'], # Note that we link NSS modules with '-z nodelete' so that mempools never get orphaned link_args : ['-Wl,-z,nodelete', '-shared', - '-Wl,--version-script=' + version_script_arg, - '-Wl,--undefined'], + '-Wl,--version-script=' + version_script_arg], link_with : [libsystemd_static, libbasic], dependencies : [threads, @@ -1620,7 +1637,8 @@ executable('systemd', include_directories : includes, link_with : [libcore, libshared], - dependencies : [threads, + dependencies : [versiondep, + threads, librt, libseccomp, libselinux, @@ -1639,7 +1657,8 @@ exe = executable('systemd-analyze', include_directories : includes, link_with : [libcore, libshared], - dependencies : [threads, + dependencies : [versiondep, + threads, librt, libseccomp, libselinux, @@ -1712,9 +1731,9 @@ executable('systemd-run-generator', executable('systemd-fstab-generator', 'src/fstab-generator/fstab-generator.c', - 'src/core/mount-setup.c', include_directories : includes, - link_with : [libshared], + link_with : [libcore_shared, + libshared], install_rpath : rootlibexecdir, install : true, install_dir : systemgeneratordir) @@ -1841,7 +1860,7 @@ if conf.get('ENABLE_LOGIND') == 1 public_programs += exe if conf.get('HAVE_PAM') == 1 - version_script_arg = join_paths(meson.source_root(), pam_systemd_sym) + version_script_arg = join_paths(project_source_root, pam_systemd_sym) pam_systemd = shared_library( 'pam_systemd', pam_systemd_c, @@ -1940,7 +1959,10 @@ else libbasic_gcrypt] endif -exe = executable('systemctl', 'src/systemctl/systemctl.c', +exe = executable('systemctl', + 'src/systemctl/systemctl.c', + 'src/systemctl/sysv-compat.h', + 'src/systemctl/sysv-compat.c', include_directories : includes, link_with : systemctl_link_with, dependencies : [threads, @@ -2185,7 +2207,8 @@ if conf.get('ENABLE_IMPORTD') == 1 systemd_pull_sources, include_directories : includes, link_with : [libshared], - dependencies : [libcurl, + dependencies : [versiondep, + libcurl, libz, libbzip2, libxz, @@ -2234,7 +2257,8 @@ if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1 systemd_journal_upload_sources, include_directories : includes, link_with : [libshared], - dependencies : [threads, + dependencies : [versiondep, + threads, libcurl, libgnutls, libxz, @@ -2350,20 +2374,18 @@ endif executable('systemd-remount-fs', 'src/remount-fs/remount-fs.c', - 'src/core/mount-setup.c', - 'src/core/mount-setup.h', include_directories : includes, - link_with : [libshared], + link_with : [libcore_shared, + libshared], install_rpath : rootlibexecdir, install : true, install_dir : rootlibexecdir) executable('systemd-machine-id-setup', 'src/machine-id-setup/machine-id-setup-main.c', - 'src/core/machine-id-setup.c', - 'src/core/machine-id-setup.h', include_directories : includes, - link_with : [libshared], + link_with : [libcore_shared, + libshared], install_rpath : rootlibexecdir, install : true, install_dir : rootbindir) @@ -2560,6 +2582,7 @@ exe = executable('systemd-stdio-bridge', 'src/stdio-bridge/stdio-bridge.c', include_directories : includes, link_with : [libshared], + dependencies : [versiondep], install_rpath : rootlibexecdir, install : true) public_programs += exe @@ -2643,7 +2666,8 @@ exe = executable('systemd-udevd', link_with : [libudev_core, libsystemd_network, libudev_static], - dependencies : [threads, + dependencies : [versiondep, + threads, libkmod, libidn, libacl, @@ -2660,7 +2684,8 @@ exe = executable('udevadm', link_with : [libudev_core, libsystemd_network, libudev_static], - dependencies : [threads, + dependencies : [versiondep, + threads, libkmod, libidn, libacl, @@ -2673,7 +2698,8 @@ public_programs += exe executable('systemd-shutdown', systemd_shutdown_sources, include_directories : includes, - link_with : [libshared], + link_with : [libcore_shared, + libshared], dependencies : [libmount], install_rpath : rootlibexecdir, install : true, @@ -2714,14 +2740,12 @@ endif exe = executable('systemd-nspawn', systemd_nspawn_sources, - 'src/core/mount-setup.c', # FIXME: use a variable? - 'src/core/mount-setup.h', - 'src/core/loopback-setup.c', - 'src/core/loopback-setup.h', include_directories : includes, - link_with : [libnspawn_core, + link_with : [libcore_shared, + libnspawn_core, libshared], - dependencies : [libblkid], + dependencies : [libblkid, + libseccomp], install_rpath : rootlibexecdir, install : true) public_programs += exe @@ -2773,7 +2797,7 @@ 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_TEST_DATA=@0@; '.format(join_paths(project_source_root, 'test')) + 'echo SYSTEMD_CATALOG_DIR=@0@; '.format(join_paths(meson.current_build_dir(), 'catalog')) + '} >@OUTPUT@'], build_by_default : true) @@ -2800,7 +2824,8 @@ foreach tuple : tests sources, include_directories : incs, link_with : link_with, - dependencies : dependencies, + dependencies : [versiondep, + dependencies], c_args : defs, build_by_default : want_tests != 'false', install_rpath : rootlibexecdir, @@ -3002,7 +3027,7 @@ foreach tuple : sanitizers test('@0@:@1@:@2@'.format(b, c, sanitizer), env, args : [exe.full_path(), - join_paths(meson.source_root(), p)]) + join_paths(project_source_root, p)]) endif endforeach endif @@ -3014,7 +3039,7 @@ endforeach if git.found() all_files = run_command( git, - ['--git-dir=@0@/.git'.format(meson.source_root()), + ['--git-dir=@0@/.git'.format(project_source_root), 'ls-files', ':/*.[ch]']) all_files = files(all_files.stdout().split()) @@ -3022,10 +3047,10 @@ if git.found() custom_target( 'tags', output : 'tags', - command : [env, 'etags', '-o', '@0@/TAGS'.format(meson.source_root())] + all_files) + command : [env, 'etags', '-o', '@0@/TAGS'.format(project_source_root)] + all_files) run_target( 'ctags', - command : [env, 'ctags', '-o', '@0@/tags'.format(meson.source_root())] + all_files) + command : [env, 'ctags', '-o', '@0@/tags'.format(project_source_root)] + all_files) endif if git.found() @@ -3038,17 +3063,17 @@ endif if git.found() git_head = run_command( git, - ['--git-dir=@0@/.git'.format(meson.source_root()), + ['--git-dir=@0@/.git'.format(project_source_root), 'rev-parse', 'HEAD']).stdout().strip() git_head_short = run_command( git, - ['--git-dir=@0@/.git'.format(meson.source_root()), + ['--git-dir=@0@/.git'.format(project_source_root), 'rev-parse', '--short=7', 'HEAD']).stdout().strip() run_target( 'git-snapshot', command : ['git', 'archive', - '-o', '@0@/systemd-@1@.tar.gz'.format(meson.source_root(), + '-o', '@0@/systemd-@1@.tar.gz'.format(project_source_root, git_head_short), '--prefix', 'systemd-@0@/'.format(git_head), 'HEAD']) diff --git a/meson_options.txt b/meson_options.txt index 044bb792b..d4ec37dda 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,7 +11,7 @@ option('split-bin', type : 'combo', choices : ['auto', 'true', 'false'], option('rootlibdir', type : 'string', description : '''[/usr]/lib/x86_64-linux-gnu or such''') option('rootprefix', type : 'string', - description : '''override the root prefix''') + description : '''override the root prefix [default '/' if split-usr and '/usr' otherwise]''') option('link-udev-shared', type : 'boolean', description : 'link systemd-udev and its helpers to libsystemd-shared.so') option('link-systemctl-shared', type: 'boolean', @@ -213,7 +213,7 @@ option('dns-over-tls', type : 'combo', choices : ['auto', 'gnutls', 'openssl', ' description : 'DNS-over-TLS support') option('dns-servers', type : 'string', description : 'space-separated list of default DNS servers', - value : '8.8.8.8 8.8.4.4 2001:4860:4860::8888 2001:4860:4860::8844') + value : '1.1.1.1 8.8.8.8 1.0.0.1 8.8.4.4 2606:4700:4700::1111 2001:4860:4860::8888 2606:4700:4700::1001 2001:4860:4860::8844') option('ntp-servers', type : 'string', description : 'space-separated list of default NTP servers', value : 'time1.google.com time2.google.com time3.google.com time4.google.com') diff --git a/mkosi.build b/mkosi.build index 114e617c8..16ac2e9d0 100755 --- a/mkosi.build +++ b/mkosi.build @@ -95,3 +95,7 @@ EOF 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 + +mkdir -p "$DESTDIR"/efi/EFI/systemd "$DESTDIR"/efi/EFI/BOOT +cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/efi/EFI/systemd/systemd-bootx64.efi +cp "$DESTDIR"/usr/lib/systemd/boot/efi/systemd-bootx64.efi "$DESTDIR"/efi/EFI/BOOT/bootx64.efi diff --git a/po/fr.po b/po/fr.po index ca3590160..3f0997885 100644 --- a/po/fr.po +++ b/po/fr.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-11-02 14:28+0100\n" -"PO-Revision-Date: 2018-11-02 14:32+0100\n" +"POT-Creation-Date: 2019-03-07 22:43+0100\n" +"PO-Revision-Date: 2019-03-07 23:09+0100\n" "Last-Translator: Sylvain Plantefève \n" "Language-Team: French\n" "Language: fr\n" @@ -489,10 +489,9 @@ msgstr "" "actives." #: src/login/org.freedesktop.login1.policy:341 -msgid "Allow indication to the firmware to boot to setup interface" +msgid "Indicate to the firmware to boot to setup interface" msgstr "" -"Permet d'indiquer au micrologiciel de démarrer sur l'interface de " -"configuration" +"Indiquer au micrologiciel de démarrer sur l'interface de configuration" #: src/login/org.freedesktop.login1.policy:342 msgid "" @@ -502,11 +501,35 @@ msgstr "" "Authentification requise pour indiquer au micrologiciel de démarrer sur " "l'interface de configuration." -#: src/login/org.freedesktop.login1.policy:351 +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "Indiquer au programme d'amorçage d'afficher le menu au démarrage" + +#: src/login/org.freedesktop.login1.policy:353 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"Authentification requise pour indiquer au programme d'amorçage d'afficher " +"le menu au démarrage." + +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "Indiquer au programme d'amorçage de démarrer une entrée spécifique" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"Authentification requise pour indiquer au programme d'amorçage de démarrer " +"une entrée spécifique." + +#: src/login/org.freedesktop.login1.policy:374 msgid "Set a wall message" msgstr "Définir un message wall" -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:375 msgid "Authentication is required to set a wall message" msgstr "Authentification requise pour définir un message wall." diff --git a/po/ja.po b/po/ja.po index 731657d69..e11bbadf3 100644 --- a/po/ja.po +++ b/po/ja.po @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd\n" -"Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2018-10-27 07:32+0900\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2019-04-01 11:27+0900\n" "PO-Revision-Date: 2018-10-27 07:41+0900\n" "Last-Translator: Yu Watanabe \n" "Language-Team: \n" @@ -38,7 +38,9 @@ msgstr "システムサービスファイルやその他のユニットファイ #: src/core/org.freedesktop.systemd1.policy.in:44 msgid "Authentication is required to manage system service or unit files." -msgstr "システムサービスファイルやその他のユニットファイルを管理するには認証が必要です。" +msgstr "" +"システムサービスファイルやその他のユニットファイルを管理するには認証が必要で" +"す。" #: src/core/org.freedesktop.systemd1.policy.in:54 msgid "Set or unset system and service manager environment variables" @@ -114,7 +116,8 @@ msgstr "仮想マシンもしくはコンテナイメージのダウンロード #: src/import/org.freedesktop.import1.policy:43 msgid "Authentication is required to download a VM or container image" -msgstr "仮想マシンもしくはコンテナイメージをダウンロードするには認証が必要です。" +msgstr "" +"仮想マシンもしくはコンテナイメージをダウンロードするには認証が必要です。" #: src/locale/org.freedesktop.locale1.policy:22 msgid "Set system locale" @@ -139,7 +142,8 @@ msgstr "アプリケーションがシステムのシャットダウンを阻害 #: src/login/org.freedesktop.login1.policy:23 msgid "" "Authentication is required for an application to inhibit system shutdown." -msgstr "アプリケーションがシステムのシャットダウンを阻害するには認証が必要です。" +msgstr "" +"アプリケーションがシステムのシャットダウンを阻害するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:33 msgid "Allow applications to delay system shutdown" @@ -147,7 +151,8 @@ msgstr "アプリケーションがシステムのシャットダウンを遅延 #: src/login/org.freedesktop.login1.policy:34 msgid "Authentication is required for an application to delay system shutdown." -msgstr "アプリケーションがシステムのシャットダウンを遅延させるには認証が必要です。" +msgstr "" +"アプリケーションがシステムのシャットダウンを遅延させるには認証が必要です。" #: src/login/org.freedesktop.login1.policy:44 msgid "Allow applications to inhibit system sleep" @@ -173,7 +178,8 @@ msgstr "アプリケーションがシステムの自動的なサスペンドを msgid "" "Authentication is required for an application to inhibit automatic system " "suspend." -msgstr "アプリケーションがシステムの自動的なサスペンドを阻害するには認証が必要です。" +msgstr "" +"アプリケーションがシステムの自動的なサスペンドを阻害するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:75 msgid "Allow applications to inhibit system handling of the power key" @@ -193,7 +199,8 @@ msgstr "アプリケーションがサスペンドキーによる動作を阻害 msgid "" "Authentication is required for an application to inhibit system handling of " "the suspend key." -msgstr "アプリケーションがサスペンドキーによる動作を阻害するには認証が必要です。" +msgstr "" +"アプリケーションがサスペンドキーによる動作を阻害するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:97 msgid "Allow applications to inhibit system handling of the hibernate key" @@ -203,7 +210,8 @@ msgstr "アプリケーションがハイバネートキーによる動作を阻 msgid "" "Authentication is required for an application to inhibit system handling of " "the hibernate key." -msgstr "アプリケーションがハイバネートキーによる動作を阻害するには認証が必要です。" +msgstr "" +"アプリケーションがハイバネートキーによる動作を阻害するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:107 msgid "Allow applications to inhibit system handling of the lid switch" @@ -221,7 +229,8 @@ msgstr "ログインしていないユーザがプログラムを実行するこ #: src/login/org.freedesktop.login1.policy:118 msgid "Explicit request is required to run programs as a non-logged-in user." -msgstr "ログインしていないユーザがプログラムを実行するには明示的な要求が必要です。" +msgstr "" +"ログインしていないユーザがプログラムを実行するには明示的な要求が必要です。" #: src/login/org.freedesktop.login1.policy:127 msgid "Allow non-logged-in users to run programs" @@ -264,7 +273,8 @@ msgstr "他のユーザがログインしている状態でシステムの電源 msgid "" "Authentication is required for powering off the system while other users are " "logged in." -msgstr "他のユーザがログインしている状態でシステムの電源を切るには認証が必要です。" +msgstr "" +"他のユーザがログインしている状態でシステムの電源を切るには認証が必要です。" #: src/login/org.freedesktop.login1.policy:180 msgid "Power off the system while an application asked to inhibit it" @@ -274,7 +284,9 @@ msgstr "アプリケーションが使用されている状態でシステムの msgid "" "Authentication is required for powering off the system while an application " "asked to inhibit it." -msgstr "アプリケーションが使用されている状態でシステムの電源を切るには認証が必要です。" +msgstr "" +"アプリケーションが使用されている状態でシステムの電源を切るには認証が必要で" +"す。" #: src/login/org.freedesktop.login1.policy:191 msgid "Reboot the system" @@ -292,7 +304,8 @@ msgstr "他のユーザがログインしている状態でシステムを再起 msgid "" "Authentication is required for rebooting the system while other users are " "logged in." -msgstr "他のユーザがログインしている状態でシステムを再起動するには認証が必要です。" +msgstr "" +"他のユーザがログインしている状態でシステムを再起動するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:213 msgid "Reboot the system while an application asked to inhibit it" @@ -302,7 +315,9 @@ msgstr "アプリケーションが使用されている状態でシステムを msgid "" "Authentication is required for rebooting the system while an application " "asked to inhibit it." -msgstr "アプリケーションが使用されている状態でシステムを再起動するには認証が必要です。" +msgstr "" +"アプリケーションが使用されている状態でシステムを再起動するには認証が必要で" +"す。" #: src/login/org.freedesktop.login1.policy:224 msgid "Halt the system" @@ -320,7 +335,8 @@ msgstr "他のユーザがログインしている状態でシステムを停止 msgid "" "Authentication is required for halting the system while other users are " "logged in." -msgstr "他のユーザがログインしている状態でシステムを停止するには認証が必要です。" +msgstr "" +"他のユーザがログインしている状態でシステムを停止するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:246 msgid "Halt the system while an application asked to inhibit it" @@ -330,7 +346,8 @@ msgstr "アプリケーションが使用されている状態でシステムを msgid "" "Authentication is required for halting the system while an application asked " "to inhibit it." -msgstr "アプリケーションが使用されている状態でシステムを停止するには認証が必要です。" +msgstr "" +"アプリケーションが使用されている状態でシステムを停止するには認証が必要です。" #: src/login/org.freedesktop.login1.policy:257 msgid "Suspend the system" @@ -348,7 +365,9 @@ msgstr "他のユーザがログインしている状態でシステムをサス msgid "" "Authentication is required for suspending the system while other users are " "logged in." -msgstr "他のユーザがログインしている状態でシステムをサスペンドするには認証が必要です。" +msgstr "" +"他のユーザがログインしている状態でシステムをサスペンドするには認証が必要で" +"す。" #: src/login/org.freedesktop.login1.policy:278 msgid "Suspend the system while an application asked to inhibit it" @@ -358,7 +377,9 @@ msgstr "アプリケーションが使用されている状態でシステムを msgid "" "Authentication is required for suspending the system while an application " "asked to inhibit it." -msgstr "アプリケーションが使用されている状態でシステムをサスペンドするには認証が必要です。" +msgstr "" +"アプリケーションが使用されている状態でシステムをサスペンドするには認証が必要" +"です。" #: src/login/org.freedesktop.login1.policy:289 msgid "Hibernate the system" @@ -376,7 +397,9 @@ msgstr "他のユーザがログインしている状態でシステムをハイ msgid "" "Authentication is required for hibernating the system while other users are " "logged in." -msgstr "他のユーザがログインしている状態でシステムをハイバネートするには認証が必要です。" +msgstr "" +"他のユーザがログインしている状態でシステムをハイバネートするには認証が必要で" +"す。" #: src/login/org.freedesktop.login1.policy:310 msgid "Hibernate the system while an application asked to inhibit it" @@ -386,7 +409,9 @@ msgstr "アプリケーションが使用されている状態でシステムを msgid "" "Authentication is required for hibernating the system while an application " "asked to inhibit it." -msgstr "アプリケーションが使用されている状態でシステムをハイバネートするには認証が必要です。" +msgstr "" +"アプリケーションが使用されている状態でシステムをハイバネートするには認証が必" +"要です。" #: src/login/org.freedesktop.login1.policy:321 msgid "Manage active sessions, users and seats" @@ -403,23 +428,56 @@ msgstr "アクティブなセッションのロックもしくはアンロック #: src/login/org.freedesktop.login1.policy:332 msgid "Authentication is required to lock or unlock active sessions." -msgstr "アクティブなセッションをロックもしくはアンロックするには認証が必要です。" +msgstr "" +"アクティブなセッションをロックもしくはアンロックするには認証が必要です。" #: src/login/org.freedesktop.login1.policy:341 -msgid "Allow indication to the firmware to boot to setup interface" -msgstr "ファームウェアに「インターフェースの設定を起動」を表示する" +msgid "Set the reboot \"reason\" in the kernel" +msgstr "再起動の理由を設定する" #: src/login/org.freedesktop.login1.policy:342 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "再起動の理由を設定するには認証が必要です。" + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "ファームウェアに「インターフェースの設定を起動」を表示させる" + +#: src/login/org.freedesktop.login1.policy:353 msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." -msgstr "ファームウェアに「インターフェースの設定を起動」を表示するには認証が必要です。" +msgstr "" +"ファームウェアに「インターフェースの設定を起動」を表示させるには認証が必要で" +"す。" -#: src/login/org.freedesktop.login1.policy:351 +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "ブートローダにブートローダメニューを起動するための項目を表示させる。" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"ブートローダにブートローダメニューを起動するための項目を表示させるには認証が" +"必要です。" + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "ブートローダに特定の項目を表示させる" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "ブートローダに特定の項目を表示させるには認証が必要です。" + +#: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" msgstr "全ユーザへのメッセージの設定" -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:386 msgid "Authentication is required to set a wall message" msgstr "全ユーザへのメッセージを設定するには認証が必要です。" @@ -557,7 +615,9 @@ msgstr "ハードウェア時刻をローカルタイムゾーンもしくはUTC msgid "" "Authentication is required to control whether the RTC stores the local or " "UTC time." -msgstr "ハードウェア時刻をローカルタイムゾーンもしくはUTCに設定するには認証が必要です。" +msgstr "" +"ハードウェア時刻をローカルタイムゾーンもしくはUTCに設定するには認証が必要で" +"す。" #: src/timedate/org.freedesktop.timedate1.policy:53 msgid "Turn network time synchronization on or off" @@ -569,32 +629,32 @@ msgid "" "shall be enabled." msgstr "ネットワーク経由の時刻同期を有効もしくは無効にするには認証が必要です。" -#: src/core/dbus-unit.c:326 +#: src/core/dbus-unit.c:316 msgid "Authentication is required to start '$(unit)'." msgstr "'$(unit)'を開始するには認証が必要です。" -#: src/core/dbus-unit.c:327 +#: src/core/dbus-unit.c:317 msgid "Authentication is required to stop '$(unit)'." msgstr "'$(unit)'を停止するには認証が必要です。" -#: src/core/dbus-unit.c:328 +#: src/core/dbus-unit.c:318 msgid "Authentication is required to reload '$(unit)'." msgstr "'$(unit)'を再読込するには認証が必要です。" -#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330 +#: src/core/dbus-unit.c:319 src/core/dbus-unit.c:320 msgid "Authentication is required to restart '$(unit)'." msgstr "'$(unit)'を再起動するには認証が必要です。" -#: src/core/dbus-unit.c:437 +#: src/core/dbus-unit.c:492 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." msgstr "'$(unit)'のプロセスにUNIXシグナルを送るには認証が必要です。" -#: src/core/dbus-unit.c:468 +#: src/core/dbus-unit.c:523 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "'$(unit)'の「失敗」状態をリセットするには認証が必要です。" -#: src/core/dbus-unit.c:501 +#: src/core/dbus-unit.c:556 msgid "Authentication is required to set properties on '$(unit)'." msgstr "'$(unit)'のプロパティを設定するには認証が必要です。" diff --git a/po/lt.po b/po/lt.po index 13e8f3e74..3790f9c14 100644 --- a/po/lt.po +++ b/po/lt.po @@ -3,15 +3,15 @@ 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" +"POT-Creation-Date: 2019-04-08 15:29+0000\n" +"PO-Revision-Date: 2019-04-08 22:01+0300\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" +"X-Generator: Poedit 2.2.1\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" @@ -468,11 +468,20 @@ 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ą" +msgid "Set the reboot \"reason\" in the kernel" +msgstr "Nustatyti paleidimo iš naujo \"priežastį\" branduolyje" #: src/login/org.freedesktop.login1.policy:342 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "" +"Norint nustatyti paleidimo iš naujo \"priežastį\" branduolyje, reikia " +"nustatyti tapatybę." + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "Nurodyti programinei aparatinei įrangai pasileisti į sąrankos sąsają" + +#: src/login/org.freedesktop.login1.policy:353 msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." @@ -480,11 +489,35 @@ msgstr "" "Norint nurodyti programinei aparatinei įrangai pasileisti į sąrankos sąsają, " "reikia nustatyti tapatybę." -#: src/login/org.freedesktop.login1.policy:351 +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "Nurodyti pradiniam įkėlikliui paleisti pradinio įkėliklio meniu" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"Norint nurodyti pradiniam įkėlikliui paleisti pradinio įkėliklio meniu, " +"reikia nustatyti tapatybę." + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "Nurodyti pradiniam įkėlikliui paleisti tam tikrą įrašą" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"Norint nurodyti pradiniam įkėlikliui paleisti tam tikrą pradinio įkėliklio " +"įrašą, reikia nustatyti tapatybę." + +#: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" msgstr "Nustatyti sienos pranešimą" -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:386 msgid "Authentication is required to set a wall message" msgstr "Norint nustatyti sienos pranešimą, reikia nustatyti tapatybę" @@ -654,36 +687,36 @@ msgstr "" "Norint valdyti ar tinklo laiko sinchronizavimas turėtų būti įjungtas, reikia " "nustatyti tapatybę." -#: src/core/dbus-unit.c:326 +#: src/core/dbus-unit.c:316 msgid "Authentication is required to start '$(unit)'." msgstr "Norint paleisti \"$(unit)\", reikia nustatyti tapatybę." -#: src/core/dbus-unit.c:327 +#: src/core/dbus-unit.c:317 msgid "Authentication is required to stop '$(unit)'." msgstr "Norint stabdyti \"$(unit)\", reikia nustatyti tapatybę." -#: src/core/dbus-unit.c:328 +#: src/core/dbus-unit.c:318 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 +#: src/core/dbus-unit.c:319 src/core/dbus-unit.c:320 msgid "Authentication is required to restart '$(unit)'." msgstr "Norint paleisti \"$(unit)\" iš naujo, reikia nustatyti tapatybę." -#: src/core/dbus-unit.c:437 +#: src/core/dbus-unit.c:492 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 +#: src/core/dbus-unit.c:523 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 +#: src/core/dbus-unit.c:556 msgid "Authentication is required to set properties on '$(unit)'." msgstr "Norint nustatyti \"$(unit)\" savybes, reikia nustatyti tapatybę." diff --git a/po/pl.po b/po/pl.po index 80c9c97e9..4850bdaca 100644 --- a/po/pl.po +++ b/po/pl.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: systemd\n" "Report-Msgid-Bugs-To: https://github.com/systemd/systemd/issues\n" -"POT-Creation-Date: 2018-10-26 19:14+0000\n" -"PO-Revision-Date: 2018-10-26 21:15+0200\n" +"POT-Creation-Date: 2019-03-26 15:29+0000\n" +"PO-Revision-Date: 2019-03-26 17:28+0100\n" "Last-Translator: Piotr Drąg \n" "Language-Team: Polish \n" "Language: pl\n" @@ -469,10 +469,20 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby zablokować lub odblokować aktywne sesje." #: src/login/org.freedesktop.login1.policy:341 -msgid "Allow indication to the firmware to boot to setup interface" -msgstr "Wskazanie oprogramowaniu sprzętowemu, aby uruchomić interfejs ustawień" +msgid "Set the reboot \"reason\" in the kernel" +msgstr "Ustawienie przyczyny ponownego uruchomienia w jądrze" #: src/login/org.freedesktop.login1.policy:342 +msgid "Authentication is required to set the reboot \"reason\" in the kernel." +msgstr "" +"Wymagane jest uwierzytelnienie, aby ustawić przyczynę ponownego uruchomienia " +"w jądrze." + +#: src/login/org.freedesktop.login1.policy:352 +msgid "Indicate to the firmware to boot to setup interface" +msgstr "Wskazanie oprogramowaniu sprzętowemu, aby uruchomić interfejs ustawień" + +#: src/login/org.freedesktop.login1.policy:353 msgid "" "Authentication is required to indicate to the firmware to boot to setup " "interface." @@ -480,11 +490,35 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby wskazać oprogramowaniu sprzętowemu, że " "należy uruchomić interfejs ustawień." -#: src/login/org.freedesktop.login1.policy:351 +#: src/login/org.freedesktop.login1.policy:363 +msgid "Indicate to the boot loader to boot to the boot loader menu" +msgstr "Wskazanie programowi startowemu, aby uruchomić jego menu" + +#: src/login/org.freedesktop.login1.policy:364 +msgid "" +"Authentication is required to indicate to the boot loader to boot to the " +"boot loader menu." +msgstr "" +"Wymagane jest uwierzytelnienie, aby wskazać programowi startowemu, że należy " +"uruchomić jego menu." + +#: src/login/org.freedesktop.login1.policy:374 +msgid "Indicate to the boot loader to boot a specific entry" +msgstr "Wskazanie programowi startowemu, aby uruchomić podany wpis" + +#: src/login/org.freedesktop.login1.policy:375 +msgid "" +"Authentication is required to indicate to the boot loader to boot into a " +"specific boot loader entry." +msgstr "" +"Wymagane jest uwierzytelnienie, aby wskazać programowi startowemu, że należy " +"uruchomić podany wpis." + +#: src/login/org.freedesktop.login1.policy:385 msgid "Set a wall message" msgstr "Ustawienie komunikatu wall" -#: src/login/org.freedesktop.login1.policy:352 +#: src/login/org.freedesktop.login1.policy:386 msgid "Authentication is required to set a wall message" msgstr "Wymagane jest uwierzytelnienie, aby ustawić komunikat wall" @@ -653,25 +687,25 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby kontrolować, czy włączyć synchronizację " "czasu przez sieć." -#: src/core/dbus-unit.c:326 +#: src/core/dbus-unit.c:325 msgid "Authentication is required to start '$(unit)'." msgstr "Wymagane jest uwierzytelnienie, aby uruchomić jednostkę „$(unit)”." -#: src/core/dbus-unit.c:327 +#: src/core/dbus-unit.c:326 msgid "Authentication is required to stop '$(unit)'." msgstr "Wymagane jest uwierzytelnienie, aby zatrzymać jednostkę „$(unit)”." -#: src/core/dbus-unit.c:328 +#: src/core/dbus-unit.c:327 msgid "Authentication is required to reload '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie wczytać jednostkę „$(unit)”." -#: src/core/dbus-unit.c:329 src/core/dbus-unit.c:330 +#: src/core/dbus-unit.c:328 src/core/dbus-unit.c:329 msgid "Authentication is required to restart '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ponownie uruchomić jednostkę „$(unit)”." -#: src/core/dbus-unit.c:437 +#: src/core/dbus-unit.c:434 msgid "" "Authentication is required to send a UNIX signal to the processes of " "'$(unit)'." @@ -679,13 +713,13 @@ msgstr "" "Wymagane jest uwierzytelnienie, aby wysłać sygnał uniksowy do procesów " "jednostki „$(unit)”." -#: src/core/dbus-unit.c:468 +#: src/core/dbus-unit.c:465 msgid "Authentication is required to reset the \"failed\" state of '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby przywrócić stan „failed” (niepowodzenia) " "jednostki „$(unit)”." -#: src/core/dbus-unit.c:501 +#: src/core/dbus-unit.c:498 msgid "Authentication is required to set properties on '$(unit)'." msgstr "" "Wymagane jest uwierzytelnienie, aby ustawić właściwości jednostki „$(unit)”." diff --git a/rules/60-block.rules b/rules/60-block.rules index a1458e918..491081f0b 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*|dasd*", OPTIONS+="watch" +ACTION!="remove", SUBSYSTEM=="block", KERNEL=="loop*|nvme*|sd*|vd*|xvd*|pmem*|mmcblk*|dasd*|nbd*", OPTIONS+="watch" diff --git a/rules/99-systemd.rules.in b/rules/99-systemd.rules.in index 419ca4ed8..f98263176 100644 --- a/rules/99-systemd.rules.in +++ b/rules/99-systemd.rules.in @@ -56,6 +56,8 @@ 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" +SUBSYSTEM=="udc", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}+="usb-gadget.target" + # Apply sysctl variables to network devices (and only to those) as they appear. ACTION=="add", SUBSYSTEM=="net", KERNEL!="lo", RUN+="@rootlibexecdir@/systemd-sysctl --prefix=/net/ipv4/conf/$name --prefix=/net/ipv4/neigh/$name --prefix=/net/ipv6/conf/$name --prefix=/net/ipv6/neigh/$name" diff --git a/semaphoreci/gcc-compilation.sh b/semaphoreci/gcc-compilation.sh index 0e0c83788..8755b850b 100755 --- a/semaphoreci/gcc-compilation.sh +++ b/semaphoreci/gcc-compilation.sh @@ -2,7 +2,42 @@ set -ex -meson build -Dtests=unsafe -Dsplit-usr=true -Dslow-tests=true -ninja -C build -ninja -C build test -DESTDIR=/var/tmp/inst1 ninja -C build install +# keep this in sync with setup.sh +CONTAINER=${RELEASE:-buster}-${ARCH:-amd64} +AUTOPKGTESTDIR=${SEMAPHORE_CACHE_DIR:-/tmp}/autopkgtest +# semaphore cannot expose these, but useful for interactive/local runs +ARTIFACTS_DIR=/tmp/artifacts + +# add current debian/ packaging +git fetch --depth=1 https://salsa.debian.org/systemd-team/systemd.git master +git checkout FETCH_HEAD debian + +# craft changelog +UPSTREAM_VER=$(git describe | sed 's/^v//') +cat << EOF > debian/changelog.new +systemd (${UPSTREAM_VER}-0) UNRELEASED; urgency=low + + * Automatic build for upstream test + + -- systemd test $(date -R) + +EOF +cat debian/changelog >> debian/changelog.new +mv debian/changelog.new debian/changelog + +# clean out patches +rm -rf debian/patches +# disable autopkgtests which are not for upstream +sed -i '/# NOUPSTREAM/ q' debian/tests/control +# enable more unit tests +sed -i '/^CONFFLAGS =/ s/=/= -Dtests=unsafe -Dsplit-usr=true -Dslow-tests=true /' debian/rules +# no orig tarball +echo '1.0' > debian/source/format + +# build source package +dpkg-buildpackage -S -I -I$(basename "$SEMAPHORE_CACHE_DIR") -d -us -uc -nc + +# now build the package and run the tests +rm -rf "$ARTIFACTS_DIR" +# autopkgtest exits with 2 for "some tests skipped", accept that +$AUTOPKGTESTDIR/runner/autopkgtest --apt-upgrade --env DEB_BUILD_OPTIONS=noudeb --env TEST_UPSTREAM=1 ../systemd_*.dsc -o "$ARTIFACTS_DIR" -- lxc -s $CONTAINER || [ $? -eq 2 ] diff --git a/semaphoreci/setup.sh b/semaphoreci/setup.sh index d9a7b5068..c904a300a 100755 --- a/semaphoreci/setup.sh +++ b/semaphoreci/setup.sh @@ -2,26 +2,57 @@ set -ex -sudo add-apt-repository ppa:upstream-systemd-ci/systemd-ci -y -sudo rm -rf /etc/apt/sources.list.d/beineri* /etc/apt/sources.list.d/google-chrome* /etc/apt/sources.list.d/heroku* /etc/apt/sources.list.d/mongodb* /etc/apt/sources.list.d/webupd8team* /etc/apt/sources.list.d/rwky* /etc/apt/sources.list.d/rethinkdb* /etc/apt/sources.list.d/cassandra* /etc/apt/sources.list.d/cwchien* /etc/apt/sources.list.d/rabbitmq* /etc/apt/sources.list.d/docker* /home/runner/{.npm,.phpbrew,.phpunit,.kerl,.kiex,.lein,.nvm,.npm,.phpbrew,.rbenv} -sudo bash -c "echo 'deb-src http://de.archive.ubuntu.com/ubuntu/ xenial main restricted universe multiverse' >>/etc/apt/sources.list" -sudo apt-get update -qq -sudo apt-get build-dep systemd -y -sudo apt-get install --force-yes -y util-linux libmount-dev libblkid-dev liblzma-dev libqrencode-dev libmicrohttpd-dev iptables-dev liblz4-dev libcurl4-gnutls-dev unifont clang-3.6 libasan0 itstool kbd cryptsetup-bin net-tools isc-dhcp-client iputils-ping strace qemu-system-x86 linux-image-virtual mount libgpg-error-dev libxkbcommon-dev python-lxml python3-lxml python3-pip libcap-dev -# curl -s https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - -# sudo add-apt-repository -y 'deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty main' -# sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test -sudo apt-get update -sudo apt-get install --force-yes -y gettext python3-evdev python3-pyparsing libmount-dev -# sudo apt-get install -y clang-6.0 -sudo sh -c 'echo 01010101010101010101010101010101 >/etc/machine-id' -sudo mount -t tmpfs none /tmp -test -d /run/mount || sudo mkdir /run/mount -sudo adduser --system --no-create-home nfsnobody -sudo rm -f /etc/mtab -git clone https://github.com/ninja-build/ninja -cd ninja -./configure.py --bootstrap -sudo cp ninja /usr/bin/ -cd .. -pip3 install --user 'meson == 0.46.1' +# default to Debian testing +DISTRO=${DISTRO:-debian} +RELEASE=${RELEASE:-buster} +ARCH=${ARCH:-amd64} +CONTAINER=${RELEASE}-${ARCH} +MAX_CACHE_AGE=604800 # one week +CACHE=${SEMAPHORE_CACHE_DIR:=/tmp}/${CONTAINER}.img.tar.gz + +create_container() { + # create autopkgtest LXC image; this sometimes fails with "Unable to fetch + # GPG key from keyserver", so retry a few times + for retry in $(seq 5); do + sudo lxc-create -n $CONTAINER -t download -- -d $DISTRO -r $RELEASE -a $ARCH && break + sleep $((retry*retry)) + done + + # unconfine the container, otherwise some tests fail + echo 'lxc.apparmor.profile = unconfined' | sudo tee -a /var/lib/lxc/$CONTAINER/config + + sudo lxc-start -n $CONTAINER + + # enable source repositories so that apt-get build-dep works + sudo lxc-attach -n $CONTAINER -- sh -ex <> /etc/apt/sources.list.d/sources.list +# wait until online +while [ -z "\$(ip route list 0/0)" ]; do sleep 1; done +apt-get -q update +apt-get -y dist-upgrade +apt-get install -y eatmydata +EOF + sudo lxc-stop -n $CONTAINER + + # cache it + sudo tar cpzf "$CACHE" /var/lib/lxc/$CONTAINER +} + +# remove semaphore repos, some of them don't work and cause error messages +sudo rm -f /etc/apt/sources.list.d/* + +# enable backports for latest LXC +echo 'deb http://archive.ubuntu.com/ubuntu xenial-backports main restricted universe multiverse' | sudo tee -a /etc/apt/sources.list.d/backports.list +sudo apt-get -q update +sudo apt-get install -y -t xenial-backports lxc +sudo apt-get install -y python3-debian git dpkg-dev fakeroot + +AUTOPKGTESTDIR=$SEMAPHORE_CACHE_DIR/autopkgtest +[ -d $AUTOPKGTESTDIR ] || git clone --quiet --depth=1 https://salsa.debian.org/ci-team/autopkgtest.git "$AUTOPKGTESTDIR" + +# use cached container image, unless older than a week +if [ -e "$CACHE" ] && [ $(( $(date +%s) - $(stat -c %Y "$CACHE") )) -le $MAX_CACHE_AGE ]; then + sudo tar -C / -xpzf "$CACHE" +else + create_container +fi diff --git a/shell-completion/bash/busctl b/shell-completion/bash/busctl index 476101c24..63a7644cb 100644 --- a/shell-completion/bash/busctl +++ b/shell-completion/bash/busctl @@ -86,7 +86,8 @@ _busctl() { -q --quiet --verbose --expect-reply=no --auto-start=no --allow-interactive-authorization=no --augment-creds=no --watch-bind=yes -j' - [ARG]='--address -H --host -M --machine --match --timeout --size --json' + [ARG]='--address -H --host -M --machine --match --timeout --size --json + --destination' ) if __contains_word "--user" ${COMP_WORDS[*]}; then @@ -106,6 +107,9 @@ _busctl() { --json) comps=$( busctl --json=help 2>/dev/null ) ;; + --destination) + comps=$( __get_busnames $mode ) + ;; esac COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) return 0 @@ -121,6 +125,7 @@ _busctl() { [BUSNAME]='status monitor capture tree' [OBJECT]='introspect' [METHOD]='call' + [EMIT]='emit' [PROPERTY_GET]='get-property' [PROPERTY_SET]='set-property' ) @@ -165,6 +170,8 @@ _busctl() { else comps='' fi + elif __contains_word "$verb" ${VERBS[EMIT]}; then + comps='' elif __contains_word "$verb" ${VERBS[PROPERTY_GET]}; then if [[ $n -eq 1 ]] ; then comps=$( __get_busnames $mode) diff --git a/shell-completion/bash/journalctl b/shell-completion/bash/journalctl index bcd4533a6..3beb347e1 100644 --- a/shell-completion/bash/journalctl +++ b/shell-completion/bash/journalctl @@ -28,7 +28,7 @@ __contains_word () { __get_machines() { local a b (machinectl list-images --no-legend --no-pager; machinectl list --no-legend --no-pager; echo ".host") | \ - { while read a b; do echo " $a"; done; } | sort -u; + { while read a b; do echo " $a"; done; } | sort -u; } __syslog_priorities=(emerg alert crit err warning notice info debug) @@ -48,10 +48,17 @@ _journalctl() { -M --machine -o --output -u --unit --user-unit -p --priority --root --case-sensitive' [ARGUNKNOWN]='-c --cursor --interval -n --lines -S --since -U --until - --after-cursor --verify-key -g --grep + --after-cursor --cursor-file --verify-key -g --grep --vacuum-size --vacuum-time --vacuum-files --output-fields' ) + # Use the default completion for shell redirect operators + if __contains_word "$prev" '>' '>>' '&>'; then + compopt -o filenames + COMPREPLY=( $(compgen -f -- "$cur") ) + return 0; + fi + if __contains_word "$prev" ${OPTS[ARG]} ${OPTS[ARGUNKNOWN]}; then case $prev in --boot|-b) @@ -70,7 +77,7 @@ _journalctl() { ;; --field|-F) comps=$(journalctl --fields | sort 2>/dev/null) - ;; + ;; --machine|-M) comps=$( __get_machines ) ;; diff --git a/shell-completion/bash/systemctl.in b/shell-completion/bash/systemctl.in index 0e58e2ba4..d73f956a3 100644 --- a/shell-completion/bash/systemctl.in +++ b/shell-completion/bash/systemctl.in @@ -84,10 +84,13 @@ __get_restartable_units () { __get_stoppable_units () { # filter out masked and not-found - __filter_units_by_properties $1 ActiveState=active,CanStop=yes $( + local units=$( { __get_not_masked_unit_files $1 $2 __get_active_units $1 $2 } | sort -u ) + __filter_units_by_properties $1 ActiveState=active,CanStop=yes $units + __filter_units_by_properties $1 ActiveState=reloading,CanStop=yes $units + __filter_units_by_properties $1 ActiveState=activating,CanStop=yes $units } __get_reloadable_units () { diff --git a/shell-completion/bash/systemd-id128 b/shell-completion/bash/systemd-id128 new file mode 100644 index 000000000..cfd5438cb --- /dev/null +++ b/shell-completion/bash/systemd-id128 @@ -0,0 +1,74 @@ +# networkctl(1) completion -*- shell-script -*- +# 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. +# +# systemd is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with systemd; If not, see . + +__contains_word () { + local w word=$1; shift + for w in "$@"; do + [[ $w = "$word" ]] && return + done + return 1 +} + +_systemd_id128() { + local i verb comps + local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} + local -A OPTS=( + [STANDALONE]='-h --help --version -p --pretty' + [ARG]='-a --app-specific' + ) + + local -A VERBS=( + [STANDALONE]='new machine-id boot-id invocation-id help' + ) + + _init_completion || return + + if __contains_word "$prev" ${OPTS[ARG]}; then + case $prev in + --app-specific|-a) + comps="" + ;; + esac + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 + fi + + if [[ "$cur" = -* ]]; then + COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") ) + return 0 + fi + + for ((i=0; i < COMP_CWORD; i++)); do + if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} && + ! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then + verb=${COMP_WORDS[i]} + break + fi + done + + if [[ -z $verb ]]; then + comps=${VERBS[*]} + elif __contains_word "$verb" ${VERBS[STANDALONE]}; then + comps='' + fi + + COMPREPLY=( $(compgen -W '$comps' -- "$cur") ) + return 0 +} + +complete -F _systemd_id128 systemd-id128 diff --git a/shell-completion/zsh/_journalctl b/shell-completion/zsh/_journalctl index aa6ace048..0520e02de 100644 --- a/shell-completion/zsh/_journalctl +++ b/shell-completion/zsh/_journalctl @@ -85,6 +85,7 @@ _arguments -s \ {-h,--help}'[Show this help]' \ '--version[Show package version]' \ '--no-pager[Do not pipe output into a pager]' \ + --no-hostname"[Don't show the hostname of local log messages]" \ {-l,--full}'[Show long fields in full]' \ {-a,--all}'[Show all fields, including long and unprintable]' \ {-f,--follow}'[Follow journal]' \ @@ -104,6 +105,7 @@ _arguments -s \ {-p+,--priority=}'[Show only messages within the specified priority range]:priority:_journalctl_field_values PRIORITY' \ {-t+,--identifier=}'[Show only messages with the specified syslog identifier]:identifier:_journalctl_field_values SYSLOG_IDENTIFIER' \ {-c+,--cursor=}'[Start showing entries from the specified cursor]:cursors:_journalctl_field_values __CURSORS' \ + '--cursor-file=[Show entries using cursor store in file]:file:_files' \ '--after-cursor=[Start showing entries from after the specified cursor]:cursors:_journalctl_field_values __CURSORS' \ '--since=[Start showing entries on or newer than the specified date]:YYYY-MM-DD HH\:MM\:SS' \ '--until=[Stop showing entries on or older than the specified date]:YYYY-MM-DD HH\:MM\:SS' \ diff --git a/src/activate/activate.c b/src/activate/activate.c index 9a83bc7f2..473366fb2 100644 --- a/src/activate/activate.c +++ b/src/activate/activate.c @@ -10,6 +10,7 @@ #include "sd-daemon.h" #include "alloc-util.h" +#include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "log.h" @@ -21,6 +22,7 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" +#include "util.h" static char** arg_listen = NULL; static bool arg_accept = false; @@ -73,7 +75,9 @@ static int open_sockets(int *epoll_fd, bool accept) { except[fd] = fd; log_close(); - close_all_fds(except, 3 + n); + r = close_all_fds(except, 3 + n); + if (r < 0) + return log_error_errno(r, "Failed to close all file descriptors: %m"); } /** Note: we leak some fd's on error here. I doesn't matter @@ -267,11 +271,15 @@ static int do_accept(const char* name, char **argv, char **envp, int fd) { _cleanup_close_ int fd_accepted = -1; fd_accepted = accept4(fd, NULL, NULL, 0); - if (fd_accepted < 0) - return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd); + if (fd_accepted < 0) { + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + return 0; - getsockname_pretty(fd_accepted, &local); - getpeername_pretty(fd_accepted, true, &peer); + return log_error_errno(errno, "Failed to accept connection on fd:%d: %m", fd); + } + + (void) getsockname_pretty(fd_accepted, &local); + (void) getpeername_pretty(fd_accepted, true, &peer); log_info("Connection from %s to %s", strna(peer), strna(local)); return fork_and_exec_process(name, argv, envp, fd_accepted); diff --git a/src/analyze/analyze-security.c b/src/analyze/analyze-security.c index a007ed1da..a9a93d0f0 100644 --- a/src/analyze/analyze-security.c +++ b/src/analyze/analyze-security.c @@ -13,6 +13,7 @@ #include "locale-util.h" #include "macro.h" #include "missing.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "pretty-print.h" @@ -49,6 +50,7 @@ struct security_info { bool memory_deny_write_execute; bool no_new_privileges; char *notify_access; + bool protect_hostname; bool private_devices; bool private_mounts; @@ -73,6 +75,7 @@ struct security_info { uint64_t restrict_namespaces; bool restrict_realtime; + bool restrict_suid_sgid; char *root_directory; char *root_image; @@ -294,10 +297,8 @@ static int assess_root_directory( assert(ret_description); *ret_badness = - (isempty(info->root_directory) || - path_equal(info->root_directory, "/")) && - (isempty(info->root_image) || - path_equal(info->root_image, "/")); + empty_or_root(info->root_directory) || + empty_or_root(info->root_image); *ret_description = NULL; return 0; @@ -768,6 +769,16 @@ static const struct security_assessor security_assessor_table[] = { .assess = assess_protect_home, .default_dependencies_only = true, }, + { + .id = "ProtectHostname=", + .description_good = "Service cannot change system host/domainname", + .description_bad = "Service may change system host/domainname", + .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=", + .weight = 50, + .range = 1, + .assess = assess_bool, + .offset = offsetof(struct security_info, protect_hostname), + }, { .id = "ProtectSystem=", .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=", @@ -1136,6 +1147,16 @@ static const struct security_assessor security_assessor_table[] = { .assess = assess_bool, .offset = offsetof(struct security_info, restrict_realtime), }, + { + .id = "RestrictSUIDSGID=", + .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=", + .description_good = "SUID/SGID file creation by service is restricted", + .description_bad = "Service may create SUID/SGID files", + .weight = 1000, + .range = 1, + .assess = assess_bool, + .offset = offsetof(struct security_info, restrict_suid_sgid), + }, { .id = "RestrictNamespaces=~CLONE_NEWUSER", .url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=", @@ -1861,6 +1882,7 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_ { "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) }, + { "ProtectHostname", "b", NULL, offsetof(struct security_info, protect_hostname) }, { "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) }, @@ -1868,6 +1890,7 @@ static int acquire_security_info(sd_bus *bus, const char *name, struct security_ { "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) }, + { "RestrictSUIDSGID", "b", NULL, offsetof(struct security_info, restrict_suid_sgid) }, { "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) }, diff --git a/src/analyze/analyze-verify.c b/src/analyze/analyze-verify.c index 1d8a1ed7b..16b07cc85 100644 --- a/src/analyze/analyze-verify.c +++ b/src/analyze/analyze-verify.c @@ -202,7 +202,7 @@ static int verify_unit(Unit *u, bool check_man) { unit_dump(u, stdout, "\t"); log_unit_debug(u, "Creating %s/start job", u->id); - r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, &err, NULL); + r = manager_add_job(u->manager, JOB_START, u, JOB_REPLACE, NULL, &err, NULL); if (r < 0) log_unit_error_errno(u, r, "Failed to create %s/start: %s", u->id, bus_error_message(&err, r)); diff --git a/src/analyze/analyze.c b/src/analyze/analyze.c index 3915b6673..ac7cb0da6 100644 --- a/src/analyze/analyze.c +++ b/src/analyze/analyze.c @@ -8,6 +8,7 @@ #include #include #include +#include #include "sd-bus.h" @@ -29,6 +30,7 @@ #include "locale-util.h" #include "log.h" #include "main-func.h" +#include "nulstr-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -36,11 +38,12 @@ #if HAVE_SECCOMP # include "seccomp-util.h" #endif +#include "sort-util.h" #include "special.h" #include "strv.h" #include "strxcpyx.h" -#include "time-util.h" #include "terminal-util.h" +#include "time-util.h" #include "unit-name.h" #include "util.h" #include "verbs.h" @@ -78,6 +81,7 @@ static UnitFileScope arg_scope = UNIT_FILE_SYSTEM; static bool arg_man = true; static bool arg_generators = false; static const char *arg_root = NULL; +static unsigned arg_iterations = 1; STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep); @@ -270,14 +274,13 @@ static int acquire_boot_times(sd_bus *bus, struct boot_times **bt) { 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" - "Please try again later.\n" - "Hint: Use 'systemctl%s list-jobs' to see active jobs", - times.finish_time, - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); - return -EINPROGRESS; - } + if (times.finish_time <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINPROGRESS), + "Bootup is not yet finished (org.freedesktop.systemd1.Manager.FinishTimestampMonotonic=%"PRIu64").\n" + "Please try again later.\n" + "Hint: Use 'systemctl%s list-jobs' to see active jobs", + times.finish_time, + arg_scope == UNIT_FILE_SYSTEM ? "" : " --user"); if (arg_scope == UNIT_FILE_SYSTEM && times.security_start_time > 0) { /* security_start_time is set when systemd is not running under container environment. */ @@ -312,7 +315,6 @@ finish: } static void free_host_info(struct host_info *hi) { - if (!hi) return; @@ -385,7 +387,8 @@ static int acquire_time_data(sd_bus *bus, struct unit_times **out) { 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)); + 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); @@ -458,7 +461,8 @@ static int acquire_host_info(sd_bus *bus, struct host_info **hi) { NULL, host); if (r < 0) { - log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s", bus_error_message(&error, r)); + log_debug_errno(r, "Failed to get host information from systemd-hostnamed, ignoring: %s", + bus_error_message(&error, r)); sd_bus_error_free(&error); } @@ -472,10 +476,10 @@ manager: NULL, host); if (r < 0) - return log_error_errno(r, "Failed to get host information from systemd: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to get host information from systemd: %s", + bus_error_message(&error, r)); *hi = TAKE_PTR(host); - return 0; } @@ -1033,8 +1037,8 @@ static int analyze_critical_chain(int argc, char *argv[], void *userdata) { (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"); + puts("The time when unit became active or started is printed after the \"@\" character.\n" + "The time the unit took to start is printed after the \"+\" character.\n"); if (argc > 1) { char **name; @@ -1328,7 +1332,8 @@ static int dump(int argc, char *argv[], void *userdata) { if (r < 0) { /* fall back to Dump if DumpByFileDescriptor is not supported */ if (!IN_SET(r, -EACCES, -EBADR)) - return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s", bus_error_message(&error, r)); + return log_error_errno(r, "Failed to issue method call DumpByFileDescriptor: %s", + bus_error_message(&error, r)); return dump_fallback(bus); } @@ -1365,8 +1370,7 @@ static int cat_config(int argc, char *argv[], void *userdata) { if (!t) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "Path %s does not start with any known prefix.", - *arg); + "Path %s does not start with any known prefix.", *arg); } else t = *arg; @@ -1515,9 +1519,15 @@ static int load_kernel_syscalls(Set **ret) { /* 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"); + f = fopen("/sys/kernel/tracing/available_events", "re"); + if (!f) { + /* We tried the non-debugfs mount point and that didn't work. If it wasn't mounted, maybe the + * old debugfs mount point works? */ + 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 tracefs' available_events file: %m"); + } for (;;) { _cleanup_free_ char *line = NULL; @@ -1628,8 +1638,8 @@ static int dump_syscall_filters(int argc, char *argv[], void *userdata) { /* make sure the error appears below normal output */ fflush(stdout); - log_error("Filter set \"%s\" not found.", *name); - return -ENOENT; + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Filter set \"%s\" not found.", *name); } dump_syscall_filter(set); @@ -1670,57 +1680,80 @@ static int dump_timespan(int argc, char *argv[], void *userdata) { return EXIT_SUCCESS; } +static int test_calendar_one(usec_t n, const char *p) { + _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; + _cleanup_free_ char *t = NULL; + int r; + + r = calendar_spec_from_string(p, &spec); + if (r < 0) + return log_error_errno(r, "Failed to parse calendar specification '%s': %m", p); + + r = calendar_spec_normalize(spec); + if (r < 0) + return log_error_errno(r, "Failed to normalize calendar specification '%s': %m", p); + + r = calendar_spec_to_string(spec, &t); + if (r < 0) + return log_error_errno(r, "Failed to format calendar specification '%s': %m", p); + + if (!streq(t, p)) + printf(" Original form: %s\n", p); + + printf("Normalized form: %s\n", t); + + for (unsigned i = 0; i < arg_iterations; i++) { + char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)]; + usec_t next; + + r = calendar_spec_next_usec(spec, n, &next); + if (r == -ENOENT) { + if (i == 0) + printf(" Next elapse: %snever%s\n", + ansi_highlight_yellow(), ansi_normal()); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to determine next elapse for '%s': %m", p); + + if (i == 0) + printf(" Next elapse: %s%s%s\n", + ansi_highlight_blue(), format_timestamp(buffer, sizeof(buffer), next), ansi_normal()); + else { + int k = DECIMAL_STR_WIDTH(i+1); + + if (k < 8) + k = 8 - k; + else + k = 0; + + printf("%*sIter. #%u: %s%s%s\n", + k, "", i+1, + ansi_highlight_blue(), format_timestamp(buffer, sizeof(buffer), next), ansi_normal()); + } + + if (!in_utc_timezone()) + printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next)); + + printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next)); + + n = next; + } + + return 0; +} + static int test_calendar(int argc, char *argv[], void *userdata) { int ret = 0, r; char **p; usec_t n; - n = now(CLOCK_REALTIME); + n = now(CLOCK_REALTIME); /* We want to use the same "base" for all expressions */ STRV_FOREACH(p, strv_skip(argv, 1)) { - _cleanup_(calendar_spec_freep) CalendarSpec *spec = NULL; - _cleanup_free_ char *t = NULL; - usec_t next; - - r = calendar_spec_from_string(*p, &spec); - if (r < 0) { - ret = log_error_errno(r, "Failed to parse calendar specification '%s': %m", *p); - continue; - } - - r = calendar_spec_normalize(spec); - if (r < 0) { - ret = log_error_errno(r, "Failed to normalize calendar specification '%s': %m", *p); - continue; - } - - r = calendar_spec_to_string(spec, &t); - if (r < 0) { - ret = log_error_errno(r, "Failed to format calendar specification '%s': %m", *p); - continue; - } - - if (!streq(t, *p)) - printf(" Original form: %s\n", *p); - - printf("Normalized form: %s\n", t); - - r = calendar_spec_next_usec(spec, n, &next); - if (r == -ENOENT) - printf(" Next elapse: never\n"); - else if (r < 0) { - ret = log_error_errno(r, "Failed to determine next elapse for '%s': %m", *p); - continue; - } else { - char buffer[CONST_MAX(FORMAT_TIMESTAMP_MAX, FORMAT_TIMESTAMP_RELATIVE_MAX)]; - - printf(" Next elapse: %s\n", format_timestamp(buffer, sizeof(buffer), next)); - - if (!in_utc_timezone()) - printf(" (in UTC): %s\n", format_timestamp_utc(buffer, sizeof(buffer), next)); - - printf(" From now: %s\n", format_timestamp_relative(buffer, sizeof(buffer), next)); - } + r = test_calendar_one(n, *p); + if (ret == 0 && r < 0) + ret = r; if (*(p+1)) putchar('\n'); @@ -1827,6 +1860,7 @@ static int help(int argc, char *argv[], void *userdata) { " earlier than the latest in the branch\n" " --man[=BOOL] Do [not] check for existence of man pages\n" " --generators[=BOOL] Do [not] run unit generators (requires privileges)\n" + " --iterations=N Show the specified number of iterations\n" "\nCommands:\n" " time Print time spent in the kernel\n" " blame Print list of running units ordered by time to init\n" @@ -1870,6 +1904,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_NO_PAGER, ARG_MAN, ARG_GENERATORS, + ARG_ITERATIONS, }; static const struct option options[] = { @@ -1889,6 +1924,7 @@ static int parse_argv(int argc, char *argv[]) { { "generators", optional_argument, NULL, ARG_GENERATORS }, { "host", required_argument, NULL, 'H' }, { "machine", required_argument, NULL, 'M' }, + { "iterations", required_argument, NULL, ARG_ITERATIONS }, {} }; @@ -1988,6 +2024,13 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_ITERATIONS: + r = safe_atou(optarg, &arg_iterations); + if (r < 0) + return log_error_errno(r, "Failed to parse iterations: %s", optarg); + + break; + case '?': return -EINVAL; @@ -2000,6 +2043,10 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Option --global only makes sense with verbs dot, unit-paths, verify."); + if (streq_ptr(argv[optind], "cat-config") && arg_scope == UNIT_FILE_USER) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Option --user is not supported for cat-config right now."); + 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."); diff --git a/src/backlight/backlight.c b/src/backlight/backlight.c index 780ad56eb..dfd680539 100644 --- a/src/backlight/backlight.c +++ b/src/backlight/backlight.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "sd-device.h" #include "alloc-util.h" @@ -379,7 +383,7 @@ static int run(int argc, char *argv[]) { clamp = shall_clamp(device); r = read_one_line_file(saved, &value); - if (r == -ENOENT) { + if (IN_SET(r, -ENOENT, 0)) { const char *curval; /* Fallback to clamping current brightness or exit early if @@ -408,7 +412,7 @@ static int run(int argc, char *argv[]) { const char *value; if (validate_device(device) == 0) { - unlink(saved); + (void) unlink(saved); return 0; } diff --git a/src/basic/alloc-util.c b/src/basic/alloc-util.c index ab7a42c4e..1e4ee722f 100644 --- a/src/basic/alloc-util.c +++ b/src/basic/alloc-util.c @@ -1,11 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include #include "alloc-util.h" #include "macro.h" -#include "util.h" +#include "memory-util.h" void* memdup(const void *p, size_t l) { void *ret; @@ -27,6 +28,9 @@ void* memdup_suffix0(const void *p, size_t l) { /* The same as memdup() but place a safety NUL byte after the allocated memory */ + if (_unlikely_(l == SIZE_MAX)) /* prevent overflow */ + return NULL; + ret = malloc(l + 1); if (!ret) return NULL; @@ -45,19 +49,23 @@ void* greedy_realloc(void **p, size_t *allocated, size_t need, size_t size) { if (*allocated >= need) return *p; - newalloc = MAX(need * 2, 64u / size); - a = newalloc * size; - - /* check for overflows */ - if (a < size * need) + if (_unlikely_(need > SIZE_MAX/2)) /* Overflow check */ return NULL; + newalloc = need * 2; + if (size_multiply_overflow(newalloc, size)) + return NULL; + + a = newalloc * size; + if (a < 64) /* Allocate at least 64 bytes */ + a = 64; + q = realloc(*p, a); if (!q) return NULL; *p = q; - *allocated = newalloc; + *allocated = _unlikely_(size == 0) ? newalloc : malloc_usable_size(q) / size; return q; } diff --git a/src/basic/alloc-util.h b/src/basic/alloc-util.h index 893a1238f..9b20be477 100644 --- a/src/basic/alloc-util.h +++ b/src/basic/alloc-util.h @@ -8,6 +8,10 @@ #include "macro.h" +#if HAS_FEATURE_MEMORY_SANITIZER +# include +#endif + typedef void (*free_func_t)(void *p); /* If for some reason more than 4M are allocated on the stack, let's abort immediately. It's better than @@ -152,11 +156,17 @@ void* greedy_realloc0(void **p, size_t *allocated, size_t need, size_t size); (void*)memset(_new_, 0, _xsize_); \ }) -/* Takes inspiration from Rusts's Option::take() method: reads and returns a pointer, but at the same time resets it to - * NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ +/* Takes inspiration from Rust's Option::take() method: reads and returns a pointer, but at the same time + * resets it to NULL. See: https://doc.rust-lang.org/std/option/enum.Option.html#method.take */ #define TAKE_PTR(ptr) \ ({ \ typeof(ptr) _ptr_ = (ptr); \ (ptr) = NULL; \ _ptr_; \ }) + +#if HAS_FEATURE_MEMORY_SANITIZER +# define msan_unpoison(r, s) __msan_unpoison(r, s) +#else +# define msan_unpoison(r, s) +#endif diff --git a/src/basic/async.c b/src/basic/async.c index c45ca0184..daa95cd10 100644 --- a/src/basic/async.c +++ b/src/basic/async.c @@ -6,6 +6,7 @@ #include #include "async.h" +#include "errno-util.h" #include "fd-util.h" #include "log.h" #include "macro.h" diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index da4dd2a82..b1519cc4e 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -1628,7 +1628,7 @@ int btrfs_subvol_snapshot_fd_full( } else if (r < 0) return r; - r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK, progress_path, progress_bytes, userdata); + r = copy_directory_fd_full(old_fd, new_path, COPY_MERGE|COPY_REFLINK|COPY_SAME_MOUNT, progress_path, progress_bytes, userdata); if (r < 0) goto fallback_fail; diff --git a/src/basic/capability-util.c b/src/basic/capability-util.c index b944ee6ea..003535295 100644 --- a/src/basic/capability-util.c +++ b/src/basic/capability-util.c @@ -28,8 +28,8 @@ int have_effective_cap(int value) { if (cap_get_flag(cap, value, CAP_EFFECTIVE, &fv) < 0) return -errno; - else - return fv == CAP_SET; + + return fv == CAP_SET; } unsigned long cap_last_cap(void) { @@ -47,6 +47,13 @@ unsigned long cap_last_cap(void) { if (r >= 0) { r = safe_atolu(content, &p); if (r >= 0) { + + if (p > 63) /* Safety for the future: if one day the kernel learns more than 64 caps, + * then we are in trouble (since we, as much userspace and kernel space + * store capability masks in uint64_t types). Let's hence protect + * ourselves against that and always cap at 63 for now. */ + p = 63; + saved = p; valid = true; return p; @@ -54,21 +61,19 @@ unsigned long cap_last_cap(void) { } /* fall back to syscall-probing for pre linux-3.2 */ - p = (unsigned long) CAP_LAST_CAP; + p = MIN((unsigned long) CAP_LAST_CAP, 63U); if (prctl(PR_CAPBSET_READ, p) < 0) { - /* Hmm, look downwards, until we find one that - * works */ + /* Hmm, look downwards, until we find one that works */ for (p--; p > 0; p --) if (prctl(PR_CAPBSET_READ, p) >= 0) break; } else { - /* Hmm, look upwards, until we find one that doesn't - * work */ - for (;; p++) + /* Hmm, look upwards, until we find one that doesn't work */ + for (; p < 63; p++) if (prctl(PR_CAPBSET_READ, p+1) < 0) break; } @@ -102,13 +107,13 @@ int capability_update_inherited_set(cap_t caps, uint64_t set) { } int capability_ambient_set_apply(uint64_t set, bool also_inherit) { - unsigned long i; _cleanup_cap_free_ cap_t caps = NULL; + unsigned long i; + int r; /* Add the capabilities to the ambient set. */ if (also_inherit) { - int r; caps = cap_get_proc(); if (!caps) return -errno; @@ -267,16 +272,12 @@ int capability_bounding_set_drop_usermode(uint64_t keep) { } int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { - _cleanup_cap_free_ cap_t d = NULL; - unsigned i, j = 0; int r; - /* Unfortunately we cannot leave privilege dropping to PID 1 - * here, since we want to run as user but want to keep some - * capabilities. Since file capabilities have been introduced - * this cannot be done across exec() anymore, unless our - * binary has the capability configured in the file system, - * which we want to avoid. */ + /* Unfortunately we cannot leave privilege dropping to PID 1 here, since we want to run as user but + * want to keep some capabilities. Since file capabilities have been introduced this cannot be done + * across exec() anymore, unless our binary has the capability configured in the file system, which + * we want to avoid. */ if (setresgid(gid, gid, gid) < 0) return log_error_errno(errno, "Failed to change group ID: %m"); @@ -285,7 +286,9 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { if (r < 0) return log_error_errno(r, "Failed to drop auxiliary groups list: %m"); - /* Ensure we keep the permitted caps across the setresuid() */ + /* Ensure we keep the permitted caps across the setresuid(). Note that we do this even if we actually + * don't want to keep any capabilities, since we want to be able to drop them from the bounding set + * too, and we can only do that if we have capabilities. */ if (prctl(PR_SET_KEEPCAPS, 1) < 0) return log_error_errno(errno, "Failed to enable keep capabilities flag: %m"); @@ -295,18 +298,21 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { if (prctl(PR_SET_KEEPCAPS, 0) < 0) return log_error_errno(errno, "Failed to disable keep capabilities flag: %m"); - /* Drop all caps from the bounding set, except the ones we want */ + /* Drop all caps from the bounding set (as well as the inheritable/permitted/effective sets), except + * the ones we want to keep */ r = capability_bounding_set_drop(keep_capabilities, true); if (r < 0) return log_error_errno(r, "Failed to drop capabilities: %m"); /* Now upgrade the permitted caps we still kept to effective caps */ - d = cap_init(); - if (!d) - return log_oom(); - - if (keep_capabilities) { + if (keep_capabilities != 0) { cap_value_t bits[u64log2(keep_capabilities) + 1]; + _cleanup_cap_free_ cap_t d = NULL; + unsigned i, j = 0; + + d = cap_init(); + if (!d) + return log_oom(); for (i = 0; i < ELEMENTSOF(bits); i++) if (keep_capabilities & (1ULL << i)) @@ -315,7 +321,7 @@ int drop_privileges(uid_t uid, gid_t gid, uint64_t keep_capabilities) { /* use enough bits */ assert(i == 64 || (keep_capabilities >> i) == 0); /* don't use too many bits */ - assert(keep_capabilities & (1ULL << (i - 1))); + assert(keep_capabilities & (UINT64_C(1) << (i - 1))); if (cap_set_flag(d, CAP_EFFECTIVE, j, bits, CAP_SET) < 0 || cap_set_flag(d, CAP_PERMITTED, j, bits, CAP_SET) < 0) @@ -362,7 +368,7 @@ bool ambient_capabilities_supported(void) { } int capability_quintet_enforce(const CapabilityQuintet *q) { - _cleanup_cap_free_ cap_t c = NULL; + _cleanup_cap_free_ cap_t c = NULL, modified = NULL; int r; if (q->ambient != (uint64_t) -1) { @@ -393,7 +399,6 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { 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; @@ -426,8 +431,15 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { 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) + if (cap_get_flag(c, cv, CAP_INHERITABLE, &old_value) < 0) { + if (errno == EINVAL) /* If the kernel knows more caps than this + * version of libcap, then this will return + * EINVAL. In that case, simply ignore it, + * pretend it doesn't exist. */ + continue; + return -errno; + } new_value = (q->inheritable & m) ? CAP_SET : CAP_CLEAR; @@ -442,8 +454,12 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { 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) + if (cap_get_flag(c, cv, CAP_PERMITTED, &old_value) < 0) { + if (errno == EINVAL) + continue; + return -errno; + } new_value = (q->permitted & m) ? CAP_SET : CAP_CLEAR; @@ -458,8 +474,12 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { 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) + if (cap_get_flag(c, cv, CAP_EFFECTIVE, &old_value) < 0) { + if (errno == EINVAL) + continue; + return -errno; + } new_value = (q->effective & m) ? CAP_SET : CAP_CLEAR; @@ -472,9 +492,35 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { } } - if (changed) - if (cap_set_proc(c) < 0) + if (changed) { + /* In order to change the bounding caps, we need to keep CAP_SETPCAP for a bit + * longer. Let's add it to our list hence for now. */ + if (q->bounding != (uint64_t) -1) { + cap_value_t cv = CAP_SETPCAP; + + modified = cap_dup(c); + if (!modified) + return -ENOMEM; + + if (cap_set_flag(modified, CAP_PERMITTED, 1, &cv, CAP_SET) < 0) + return -errno; + if (cap_set_flag(modified, CAP_EFFECTIVE, 1, &cv, CAP_SET) < 0) + return -errno; + + if (cap_compare(modified, c) == 0) { + /* No change? then drop this nonsense again */ + cap_free(modified); + modified = NULL; + } + } + + /* Now, let's enforce the caps for the first time. Note that this is where we acquire + * caps in any of the sets we currently don't have. We have to do this before + * dropping the bounding caps below, since at that point we can never acquire new + * caps in inherited/permitted/effective anymore, but only lose them. */ + if (cap_set_proc(modified ?: c) < 0) return -errno; + } } if (q->bounding != (uint64_t) -1) { @@ -483,5 +529,13 @@ int capability_quintet_enforce(const CapabilityQuintet *q) { return r; } + /* If needed, let's now set the caps again, this time in the final version, which differs from what + * we have already set only in the CAP_SETPCAP bit, which we needed for dropping the bounding + * bits. This call only undoes bits and doesn't acquire any which means the bounding caps don't + * matter. */ + if (modified) + if (cap_set_proc(c) < 0) + return -errno; + return 0; } diff --git a/src/basic/capability-util.h b/src/basic/capability-util.h index 02c7d5c3e..e69b2fbb9 100644 --- a/src/basic/capability-util.h +++ b/src/basic/capability-util.h @@ -33,10 +33,12 @@ static inline void cap_free_charpp(char **p) { } #define _cleanup_cap_free_charp_ _cleanup_(cap_free_charpp) +static inline uint64_t all_capabilities(void) { + return UINT64_MAX >> (63 - cap_last_cap()); +} + static inline bool cap_test_all(uint64_t caps) { - uint64_t m; - m = (UINT64_C(1) << (cap_last_cap() + 1)) - 1; - return FLAGS_SET(caps, m); + return FLAGS_SET(caps, all_capabilities()); } bool ambient_capabilities_supported(void); diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 8ce7ccb96..fc28109db 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -223,7 +223,7 @@ int cg_kill( _cleanup_set_free_ Set *allocated_set = NULL; bool done = false; - int r, ret = 0; + int r, ret = 0, ret_log_kill = 0; pid_t my_pid; assert(sig >= 0); @@ -267,7 +267,7 @@ int cg_kill( continue; if (log_kill) - log_kill(pid, sig, userdata); + ret_log_kill = log_kill(pid, sig, userdata); /* If we haven't killed this process yet, kill * it */ @@ -278,8 +278,12 @@ int cg_kill( if (flags & CGROUP_SIGCONT) (void) kill(pid, SIGCONT); - if (ret == 0) - ret = 1; + if (ret == 0) { + if (log_kill) + ret = ret_log_kill; + else + ret = 1; + } } done = false; @@ -2867,7 +2871,7 @@ bool fd_is_cgroup_fs(int fd) { return is_cgroup_fs(&s); } -static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { +static const char *const cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { [CGROUP_CONTROLLER_CPU] = "cpu", [CGROUP_CONTROLLER_CPUACCT] = "cpuacct", [CGROUP_CONTROLLER_IO] = "io", diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 119b493dc..a39ab451b 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -167,7 +167,7 @@ typedef enum CGroupFlags { CGROUP_REMOVE = 1 << 2, } CGroupFlags; -typedef void (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); +typedef int (*cg_kill_log_func_t)(pid_t pid, int sig, void *userdata); int cg_kill(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); int cg_kill_recursive(const char *controller, const char *path, int sig, CGroupFlags flags, Set *s, cg_kill_log_func_t kill_log, void *userdata); diff --git a/src/basic/chattr-util.h b/src/basic/chattr-util.h index 7570bba2f..29070c848 100644 --- a/src/basic/chattr-util.h +++ b/src/basic/chattr-util.h @@ -1,6 +1,37 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + +#include "missing_fs.h" + +/* The chattr() flags to apply when creating a new file *before* writing to it. In particular, flags such as + * FS_NOCOW_FL don't work if applied a-posteriori. All other flags are fine (or even necessary, think + * FS_IMMUTABLE_FL!) to apply after writing to the files. */ +#define CHATTR_EARLY_FL \ + (FS_NOATIME_FL | \ + FS_COMPR_FL | \ + FS_NOCOW_FL | \ + FS_NOCOMP_FL | \ + FS_PROJINHERIT_FL) + +#define CHATTR_ALL_FL \ + (FS_NOATIME_FL | \ + FS_SYNC_FL | \ + FS_DIRSYNC_FL | \ + FS_APPEND_FL | \ + FS_COMPR_FL | \ + FS_NODUMP_FL | \ + FS_EXTENT_FL | \ + FS_IMMUTABLE_FL | \ + FS_JOURNAL_DATA_FL | \ + FS_SECRM_FL | \ + FS_UNRM_FL | \ + FS_NOTAIL_FL | \ + FS_TOPDIR_FL | \ + FS_NOCOW_FL | \ + FS_PROJINHERIT_FL) + int chattr_fd(int fd, unsigned value, unsigned mask, unsigned *previous); int chattr_path(const char *p, unsigned value, unsigned mask, unsigned *previous); diff --git a/src/basic/conf-files.c b/src/basic/conf-files.c index b70c6e50a..d010fbb26 100644 --- a/src/basic/conf-files.c +++ b/src/basic/conf-files.c @@ -17,11 +17,11 @@ #include "missing.h" #include "path-util.h" #include "set.h" +#include "sort-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" -#include "util.h" static int files_add( Hashmap *h, diff --git a/src/basic/copy.c b/src/basic/copy.c index 46e02a375..eed9cfdff 100644 --- a/src/basic/copy.c +++ b/src/basic/copy.c @@ -743,7 +743,7 @@ int copy_file_fd_full( r = copy_bytes_full(fdf, fdt, (uint64_t) -1, copy_flags, NULL, NULL, progress_bytes, userdata); - (void) copy_times(fdf, fdt); + (void) copy_times(fdf, fdt, copy_flags); (void) copy_xattr(fdf, fdt); return r; @@ -755,6 +755,7 @@ int copy_file_full( int flags, mode_t mode, unsigned chattr_flags, + unsigned chattr_mask, CopyFlags copy_flags, copy_progress_bytes_t progress_bytes, void *userdata) { @@ -770,8 +771,8 @@ int copy_file_full( return -errno; } - if (chattr_flags != 0) - (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL); + if (chattr_mask != 0) + (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL); r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata); if (r < 0) { @@ -780,6 +781,9 @@ int copy_file_full( return r; } + if (chattr_mask != 0) + (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); + if (close(fdt) < 0) { unlink_noerrno(to); return -errno; @@ -793,6 +797,7 @@ int copy_file_atomic_full( const char *to, mode_t mode, unsigned chattr_flags, + unsigned chattr_mask, CopyFlags copy_flags, copy_progress_bytes_t progress_bytes, void *userdata) { @@ -826,8 +831,8 @@ int copy_file_atomic_full( return fdt; } - if (chattr_flags != 0) - (void) chattr_fd(fdt, chattr_flags, (unsigned) -1, NULL); + if (chattr_mask != 0) + (void) chattr_fd(fdt, chattr_flags, chattr_mask & CHATTR_EARLY_FL, NULL); r = copy_file_fd_full(from, fdt, copy_flags, progress_bytes, userdata); if (r < 0) @@ -845,14 +850,16 @@ int copy_file_atomic_full( return r; } + if (chattr_mask != 0) + (void) chattr_fd(fdt, chattr_flags, chattr_mask & ~CHATTR_EARLY_FL, NULL); + t = mfree(t); return 0; } -int copy_times(int fdf, int fdt) { +int copy_times(int fdf, int fdt, CopyFlags flags) { struct timespec ut[2]; struct stat st; - usec_t crtime = 0; assert(fdf >= 0); assert(fdt >= 0); @@ -866,8 +873,12 @@ int copy_times(int fdf, int fdt) { if (futimens(fdt, ut) < 0) return -errno; - if (fd_getcrtime(fdf, &crtime) >= 0) - (void) fd_setcrtime(fdt, crtime); + if (FLAGS_SET(flags, COPY_CRTIME)) { + usec_t crtime; + + if (fd_getcrtime(fdf, &crtime) >= 0) + (void) fd_setcrtime(fdt, crtime); + } return 0; } diff --git a/src/basic/copy.h b/src/basic/copy.h index f67702188..51ea4d51e 100644 --- a/src/basic/copy.h +++ b/src/basic/copy.h @@ -14,6 +14,7 @@ typedef enum CopyFlags { COPY_REPLACE = 1 << 2, /* Replace an existing file if there's one */ COPY_SAME_MOUNT = 1 << 3, /* Don't descend recursively into other file systems, across mount point boundaries */ COPY_MERGE_EMPTY = 1 << 4, /* Merge an existing, empty directory with our new tree to copy */ + COPY_CRTIME = 1 << 5, /* Generate a user.crtime_usec xattr off the source crtime if there is one, on copying */ } CopyFlags; typedef int (*copy_progress_bytes_t)(uint64_t n_bytes, void *userdata); @@ -24,14 +25,14 @@ 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_full(const char *from, const char *to, int open_flags, mode_t mode, unsigned chattr_flags, unsigned chattr_mask, 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, unsigned chattr_mask, CopyFlags copy_flags) { + return copy_file_full(from, to, open_flags, mode, chattr_flags, chattr_mask, 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_file_atomic_full(const char *from, const char *to, mode_t mode, unsigned chattr_flags, unsigned chattr_mask, 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, unsigned chattr_mask, CopyFlags copy_flags) { + return copy_file_atomic_full(from, to, mode, chattr_flags, chattr_mask, 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); @@ -57,5 +58,5 @@ static inline int copy_bytes(int fdf, int fdt, uint64_t max_bytes, CopyFlags cop return copy_bytes_full(fdf, fdt, max_bytes, copy_flags, NULL, NULL, NULL, NULL); } -int copy_times(int fdf, int fdt); +int copy_times(int fdf, int fdt, CopyFlags flags); int copy_xattr(int fdf, int fdt); diff --git a/src/basic/def.h b/src/basic/def.h index 5be018d82..2af0b763f 100644 --- a/src/basic/def.h +++ b/src/basic/def.h @@ -1,8 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include "util.h" - #define DEFAULT_TIMEOUT_USEC (90*USEC_PER_SEC) #define DEFAULT_RESTART_USEC (100*USEC_PER_MSEC) #define DEFAULT_CONFIRM_USEC (30*USEC_PER_SEC) @@ -21,29 +19,6 @@ #define SIGNALS_CRASH_HANDLER SIGSEGV,SIGILL,SIGFPE,SIGBUS,SIGQUIT,SIGABRT #define SIGNALS_IGNORE SIGPIPE -#if HAVE_SPLIT_USR -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" \ - "/lib/kbd/keymaps/\0" -#else -#define KBD_KEYMAP_DIRS \ - "/usr/share/keymaps/\0" \ - "/usr/share/kbd/keymaps/\0" \ - "/usr/lib/kbd/keymaps/\0" -#endif - -/* Note that we use the new /run prefix here (instead of /var/run) since we require them to be aliases and that way we - * become independent of /var being mounted */ -#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket" -#define DEFAULT_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" - -#define PLYMOUTH_SOCKET { \ - .un.sun_family = AF_UNIX, \ - .un.sun_path = "\0/org/freedesktop/plymouthd", \ - } - #define NOTIFY_FD_MAX 768 #define NOTIFY_BUFFER_MAX PIPE_BUF @@ -66,12 +41,20 @@ "/usr/lib/" n "\0" \ _CONF_PATHS_SPLIT_USR_NULSTR(n) +#define CONF_PATHS_USR(n) \ + "/etc/" n, \ + "/run/" n, \ + "/usr/local/lib/" n, \ + "/usr/lib/" n + +#define CONF_PATHS(n) \ + CONF_PATHS_USR(n) \ + _CONF_PATHS_SPLIT_USR(n) + +#define CONF_PATHS_USR_STRV(n) \ + STRV_MAKE(CONF_PATHS_USR(n)) + #define CONF_PATHS_STRV(n) \ - STRV_MAKE( \ - "/etc/" n, \ - "/run/" n, \ - "/usr/local/lib/" n, \ - "/usr/lib/" n \ - _CONF_PATHS_SPLIT_USR(n)) + STRV_MAKE(CONF_PATHS(n)) #define HIGH_RLIMIT_MEMLOCK (1024ULL*1024ULL*64ULL) diff --git a/src/basic/device-nodes.c b/src/basic/device-nodes.c index 5fcdf24bd..5ebe5b248 100644 --- a/src/basic/device-nodes.c +++ b/src/basic/device-nodes.c @@ -28,7 +28,7 @@ int encode_devnode_name(const char *str, char *str_enc, size_t len) { for (i = 0, j = 0; str[i] != '\0'; i++) { int seqlen; - seqlen = utf8_encoded_valid_unichar(&str[i]); + seqlen = utf8_encoded_valid_unichar(str + i, (size_t) -1); if (seqlen > 1) { if (len-j < (size_t)seqlen) diff --git a/src/basic/env-file.c b/src/basic/env-file.c index 7f10f9ad3..a1f1308a5 100644 --- a/src/basic/env-file.c +++ b/src/basic/env-file.c @@ -559,6 +559,6 @@ int write_env_file(const char *fname, char **l) { r = -errno; } - unlink(p); + (void) unlink(p); return r; } diff --git a/src/basic/errno-util.h b/src/basic/errno-util.h new file mode 100644 index 000000000..d7a5ea771 --- /dev/null +++ b/src/basic/errno-util.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "macro.h" + +static inline void _reset_errno_(int *saved_errno) { + if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ + return; + + errno = *saved_errno; +} + +#define PROTECT_ERRNO \ + _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno + +#define UNPROTECT_ERRNO \ + do { \ + errno = _saved_errno_; \ + _saved_errno_ = -1; \ + } while (false) + +static inline int negative_errno(void) { + /* This helper should be used to shut up gcc if you know 'errno' is + * negative. Instead of "return -errno;", use "return negative_errno();" + * It will suppress bogus gcc warnings in case it assumes 'errno' might + * be 0 and thus the caller's error-handling might not be triggered. */ + assert_return(errno > 0, -EINVAL); + return -errno; +} + +/* Hint #1: ENETUNREACH happens if we try to connect to "non-existing" special IP addresses, such as ::5. + * + * Hint #2: 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 */ +static inline bool ERRNO_IS_DISCONNECT(int r) { + return IN_SET(abs(r), + ECONNABORTED, + ECONNREFUSED, + ECONNRESET, + EHOSTDOWN, + EHOSTUNREACH, + ENETDOWN, + ENETRESET, + ENETUNREACH, + ENONET, + ENOPROTOOPT, + ENOTCONN, + EPIPE, + EPROTO, + ESHUTDOWN); +} + +/* Transient errors we might get on accept() that we should ignore. As per error handling comment in + * the accept(2) man page. */ +static inline bool ERRNO_IS_ACCEPT_AGAIN(int r) { + return ERRNO_IS_DISCONNECT(r) || + IN_SET(abs(r), + EAGAIN, + EINTR, + EOPNOTSUPP); +} + +/* Resource exhaustion, could be our fault or general system trouble */ +static inline bool ERRNO_IS_RESOURCE(int r) { + return IN_SET(abs(r), + EMFILE, + ENFILE, + ENOMEM); +} diff --git a/src/basic/fd-util.c b/src/basic/fd-util.c index 3e6ef5a06..b3b840145 100644 --- a/src/basic/fd-util.c +++ b/src/basic/fd-util.c @@ -25,6 +25,10 @@ #include "util.h" #include "tmpfile-util.h" +/* The maximum number of iterations in the loop to close descriptors in the fallback case + * when /proc/self/fd/ is inaccessible. */ +#define MAX_FD_LOOP_LIMIT (1024*1024) + int close_nointr(int fd) { assert(fd >= 0); @@ -228,6 +232,13 @@ int close_all_fds(const int except[], size_t n_except) { if (max_fd < 0) return max_fd; + /* Refuse to do the loop over more too many elements. It's better to fail immediately than to + * spin the CPU for a long time. */ + if (max_fd > MAX_FD_LOOP_LIMIT) + return log_debug_errno(SYNTHETIC_ERRNO(EPERM), + "/proc/self/fd is inaccessible. Refusing to loop over %d potential fds.", + max_fd); + for (fd = 3; fd >= 0; fd = fd < max_fd ? fd + 1 : -1) { int q; diff --git a/src/basic/fd-util.h b/src/basic/fd-util.h index 4085a244d..e490753ca 100644 --- a/src/basic/fd-util.h +++ b/src/basic/fd-util.h @@ -77,18 +77,6 @@ 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 */ -/* 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) \ - IN_SET(r, ENOMEM, EMFILE, ENFILE) - int fd_move_above_stdio(int fd); int rearrange_stdio(int original_input_fd, int original_output_fd, int original_error_fd); diff --git a/src/basic/fileio.c b/src/basic/fileio.c index e18b84299..9ab2f501c 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -17,6 +17,7 @@ #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "hexdecoct.h" #include "log.h" #include "macro.h" #include "missing.h" @@ -212,7 +213,6 @@ int write_string_filef( int read_one_line_file(const char *fn, char **line) { _cleanup_fclose_ FILE *f = NULL; - int r; assert(fn); assert(line); @@ -223,8 +223,7 @@ int read_one_line_file(const char *fn, char **line) { (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - r = read_line(f, LONG_LINE_MAX, line); - return r < 0 ? r : 0; + return read_line(f, LONG_LINE_MAX, line); } int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { @@ -266,26 +265,29 @@ int verify_file(const char *fn, const char *blob, bool accept_extra_nl) { return 1; } -int read_full_stream( +int read_full_stream_full( FILE *f, + const char *filename, + ReadFullFileFlags flags, char **ret_contents, size_t *ret_size) { _cleanup_free_ char *buf = NULL; struct stat st; - size_t n, l; - int fd; + size_t n, n_next, l; + int fd, r; assert(f); assert(ret_contents); + assert(!(flags & READ_FULL_FILE_UNBASE64) || ret_size); - n = LINE_MAX; /* Start size */ + n_next = LINE_MAX; /* Start size */ fd = fileno(f); if (fd >= 0) { /* If the FILE* object is backed by an fd (as opposed to memory or such, see fmemopen(), let's * optimize our buffering) */ - if (fstat(fileno(f), &st) < 0) + if (fstat(fd, &st) < 0) return -errno; if (S_ISREG(st.st_mode)) { @@ -298,27 +300,44 @@ int read_full_stream( * size of 0. Note that we increase the size to read here by one, so that the first read attempt * already makes us notice the EOF. */ if (st.st_size > 0) - n = st.st_size + 1; + n_next = st.st_size + 1; + + if (flags & READ_FULL_FILE_SECURE) + (void) warn_file_is_world_accessible(filename, &st, NULL, 0); } } - l = 0; + n = l = 0; for (;;) { char *t; size_t k; - t = realloc(buf, n + 1); - if (!t) - return -ENOMEM; + if (flags & READ_FULL_FILE_SECURE) { + t = malloc(n_next + 1); + if (!t) { + r = -ENOMEM; + goto finalize; + } + memcpy_safe(t, buf, n); + explicit_bzero_safe(buf, n); + } else { + t = realloc(buf, n_next + 1); + if (!t) + return -ENOMEM; + } buf = t; + n = n_next; + errno = 0; k = fread(buf + l, 1, n - l, f); if (k > 0) l += k; - if (ferror(f)) - return errno > 0 ? -errno : -EIO; + if (ferror(f)) { + r = errno > 0 ? -errno : -EIO; + goto finalize; + } if (feof(f)) break; @@ -329,10 +348,18 @@ int read_full_stream( assert(l == n); /* Safety check */ - if (n >= READ_FULL_BYTES_MAX) - return -E2BIG; + if (n >= READ_FULL_BYTES_MAX) { + r = -E2BIG; + goto finalize; + } - n = MIN(n * 2, READ_FULL_BYTES_MAX); + n_next = MIN(n * 2, READ_FULL_BYTES_MAX); + } + + if (flags & READ_FULL_FILE_UNBASE64) { + buf[l++] = 0; + r = unbase64mem_full(buf, l, flags & READ_FULL_FILE_SECURE, (void **) ret_contents, ret_size); + goto finalize; } if (!ret_size) { @@ -340,8 +367,10 @@ int read_full_stream( * 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; + if (memchr(buf, 0, l)) { + r = -EBADMSG; + goto finalize; + } } buf[l] = 0; @@ -351,21 +380,27 @@ int read_full_stream( *ret_size = l; return 0; + +finalize: + if (flags & READ_FULL_FILE_SECURE) + explicit_bzero_safe(buf, n); + + return r; } -int read_full_file(const char *fn, char **contents, size_t *size) { +int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size) { _cleanup_fclose_ FILE *f = NULL; - assert(fn); + assert(filename); assert(contents); - f = fopen(fn, "re"); + f = fopen(filename, "re"); if (!f) return -errno; (void) __fsetlocking(f, FSETLOCKING_BYCALLER); - return read_full_stream(f, contents, size); + return read_full_stream_full(f, filename, flags, contents, size); } int executable_is_script(const char *path, char **interpreter) { @@ -820,3 +855,28 @@ int safe_fgetc(FILE *f, char *ret) { return 1; } + +int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line) { + struct stat _st; + + if (!filename) + return 0; + + if (!st) { + if (stat(filename, &_st) < 0) + return -errno; + st = &_st; + } + + if ((st->st_mode & S_IRWXO) == 0) + return 0; + + if (unit) + log_syntax(unit, LOG_WARNING, filename, line, 0, + "%s has %04o mode that is too permissive, please adjust the access mode.", + filename, st->st_mode & 07777); + else + log_warning("%s has %04o mode that is too permissive, please adjust the access mode.", + filename, st->st_mode & 07777); + return 0; +} diff --git a/src/basic/fileio.h b/src/basic/fileio.h index 53e3f4ef5..760e73868 100644 --- a/src/basic/fileio.h +++ b/src/basic/fileio.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include "macro.h" @@ -27,6 +28,11 @@ typedef enum { } WriteStringFileFlags; +typedef enum { + READ_FULL_FILE_SECURE = 1 << 0, + READ_FULL_FILE_UNBASE64 = 1 << 1, +} ReadFullFileFlags; + int write_string_stream_ts(FILE *f, const char *line, WriteStringFileFlags flags, struct timespec *ts); static inline int write_string_stream(FILE *f, const char *line, WriteStringFileFlags flags) { return write_string_stream_ts(f, line, flags, NULL); @@ -38,9 +44,15 @@ static inline int write_string_file(const char *fn, const char *line, WriteStrin int write_string_filef(const char *fn, WriteStringFileFlags flags, const char *format, ...) _printf_(3, 4); -int read_one_line_file(const char *fn, char **line); -int read_full_file(const char *fn, char **contents, size_t *size); -int read_full_stream(FILE *f, char **contents, size_t *size); +int read_one_line_file(const char *filename, char **line); +int read_full_file_full(const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); +static inline int read_full_file(const char *filename, char **contents, size_t *size) { + return read_full_file_full(filename, 0, contents, size); +} +int read_full_stream_full(FILE *f, const char *filename, ReadFullFileFlags flags, char **contents, size_t *size); +static inline int read_full_stream(FILE *f, char **contents, size_t *size) { + return read_full_stream_full(f, NULL, 0, contents, size); +} int verify_file(const char *fn, const char *blob, bool accept_extra_nl); @@ -76,3 +88,5 @@ static inline int read_nul_string(FILE *f, size_t limit, char **ret) { } int safe_fgetc(FILE *f, char *ret); + +int warn_file_is_world_accessible(const char *filename, struct stat *st, const char *unit, unsigned line); diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index f25bf2cbb..d1c06cf12 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -214,64 +215,109 @@ 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; + bool st_valid = false; + struct stat st; + int r; + assert(path); - /* 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. */ + /* 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. */ - 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 */ + 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; + + st_valid = true; } - if (chmod(fd_path, mode & 07777) < 0) - return -errno; + if (chmod(fd_path, mode & 07777) < 0) { + r = -errno; + + if (!st_valid && stat(fd_path, &st) < 0) + return -errno; + + if ((mode & 07777) != (st.st_mode & 07777)) + return r; + + st_valid = true; + } } - if (uid != UID_INVALID || gid != GID_INVALID) - if (chown(fd_path, uid, gid) < 0) - return -errno; + if (uid != UID_INVALID || gid != GID_INVALID) { + if (chown(fd_path, uid, gid) < 0) { + r = -errno; + + if (!st_valid && stat(fd_path, &st) < 0) + return -errno; + + if (uid != UID_INVALID && st.st_uid != uid) + return r; + if (gid != GID_INVALID && st.st_gid != gid) + return r; + } + } return 0; } int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) { + bool st_valid = false; + struct stat st; + int r; + /* 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 ((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; + + st_valid = true; } - if (fchmod(fd, mode & 0777) < 0) - return -errno; + if (fchmod(fd, mode & 07777) < 0) { + r = -errno; + + if (!st_valid && fstat(fd, &st) < 0) + return -errno; + + if ((mode & 07777) != (st.st_mode & 07777)) + return r; + + st_valid = true; + } } if (uid != UID_INVALID || gid != GID_INVALID) - if (fchown(fd, uid, gid) < 0) - return -errno; + if (fchown(fd, uid, gid) < 0) { + r = -errno; + + if (!st_valid && fstat(fd, &st) < 0) + return -errno; + + if (uid != UID_INVALID && st.st_uid != uid) + return r; + if (gid != GID_INVALID && st.st_gid != gid) + return r; + } return 0; } @@ -307,6 +353,10 @@ int fd_warn_permissions(const char *path, int fd) { if (fstat(fd, &st) < 0) return -errno; + /* Don't complain if we are reading something that is not a file, for example /dev/null */ + if (!S_ISREG(st.st_mode)) + return 0; + if (st.st_mode & 0111) log_warning("Configuration file %s is marked executable. Please remove executable permission bits. Proceeding anyway.", path); @@ -925,6 +975,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags, if (fstat(child, &st) < 0) return -errno; if ((flags & CHASE_SAFE) && + (empty_or_root(root) || (size_t)(todo - buffer) > strlen(root)) && unsafe_transition(&previous_stat, &st)) return log_unsafe_transition(fd, child, path, flags); @@ -1329,6 +1380,21 @@ int fsync_path_at(int at_fd, const char *path) { return 0; } +int syncfs_path(int atfd, const char *path) { + _cleanup_close_ int fd = -1; + + assert(path); + + fd = openat(atfd, path, O_CLOEXEC|O_RDONLY|O_NONBLOCK); + if (fd < 0) + return -errno; + + if (syncfs(fd) < 0) + return -errno; + + return 0; +} + int open_parent(const char *path, int flags, mode_t mode) { _cleanup_free_ char *parent = NULL; int fd; @@ -1345,9 +1411,9 @@ int open_parent(const char *path, int flags, mode_t mode) { /* 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) + if (FLAGS_SET(flags, O_PATH)) flags |= O_DIRECTORY; - else if ((flags & O_TMPFILE) != O_TMPFILE) + else if (!FLAGS_SET(flags, O_TMPFILE)) flags |= O_DIRECTORY|O_RDONLY; fd = open(parent, flags, mode); diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 7ad030be5..b9651205e 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -10,8 +10,8 @@ #include #include +#include "errno-util.h" #include "time-util.h" -#include "util.h" int unlink_noerrno(const char *path); @@ -108,4 +108,6 @@ 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 syncfs_path(int atfd, const char *path); + int open_parent(const char *path, int flags, mode_t mode); diff --git a/src/basic/gcrypt-util.c b/src/basic/gcrypt-util.c index f304a2bab..9bab47dd8 100644 --- a/src/basic/gcrypt-util.c +++ b/src/basic/gcrypt-util.c @@ -7,12 +7,10 @@ #include "hexdecoct.h" void initialize_libgcrypt(bool secmem) { - const char *p; if (gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P)) return; - p = gcry_check_version("1.4.5"); - assert(p); + assert_se(gcry_check_version("1.4.5")); /* Turn off "secmem". Clients which wish to make use of this * feature should initialize the library manually */ diff --git a/src/basic/glob-util.c b/src/basic/glob-util.c index 9fac676f2..32c53f8c7 100644 --- a/src/basic/glob-util.c +++ b/src/basic/glob-util.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "dirent-util.h" #include "glob-util.h" diff --git a/src/basic/hashmap.c b/src/basic/hashmap.c index 66e9e0046..c7bd7323a 100644 --- a/src/basic/hashmap.c +++ b/src/basic/hashmap.c @@ -9,6 +9,7 @@ #include "fileio.h" #include "hashmap.h" #include "macro.h" +#include "memory-util.h" #include "mempool.h" #include "process-util.h" #include "random-util.h" @@ -16,7 +17,6 @@ #include "siphash24.h" #include "string-util.h" #include "strv.h" -#include "util.h" #if ENABLE_DEBUG_HASHMAP #include @@ -1536,7 +1536,6 @@ void *internal_hashmap_first_key_and_value(HashmapBase *h, bool remove, void **r } unsigned internal_hashmap_size(HashmapBase *h) { - if (!h) return 0; @@ -1544,7 +1543,6 @@ unsigned internal_hashmap_size(HashmapBase *h) { } unsigned internal_hashmap_buckets(HashmapBase *h) { - if (!h) return 0; @@ -1904,8 +1902,7 @@ IteratedCache *iterated_cache_free(IteratedCache *cache) { if (cache) { free(cache->keys.ptr); free(cache->values.ptr); - free(cache); } - return NULL; + return mfree(cache); } diff --git a/src/basic/hashmap.h b/src/basic/hashmap.h index e16a9f9e3..41c8adb16 100644 --- a/src/basic/hashmap.h +++ b/src/basic/hashmap.h @@ -412,9 +412,11 @@ static inline char **ordered_hashmap_get_strv(OrderedHashmap *h) { DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_key); DEFINE_TRIVIAL_CLEANUP_FUNC(Hashmap*, hashmap_free_free_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free); +DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_key); DEFINE_TRIVIAL_CLEANUP_FUNC(OrderedHashmap*, ordered_hashmap_free_free_free); #define _cleanup_hashmap_free_ _cleanup_(hashmap_freep) diff --git a/src/basic/hexdecoct.c b/src/basic/hexdecoct.c index c0f96409f..132439fd1 100644 --- a/src/basic/hexdecoct.c +++ b/src/basic/hexdecoct.c @@ -8,8 +8,8 @@ #include "alloc-util.h" #include "hexdecoct.h" #include "macro.h" +#include "memory-util.h" #include "string-util.h" -#include "util.h" char octchar(int x) { return '0' + (x & 7); @@ -601,10 +601,11 @@ 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)) + if (plen >= SSIZE_MAX - 1 - slen || + lines > (SSIZE_MAX - plen - 1 - slen) / (indent + width + 1)) return -ENOMEM; - t = realloc(*prefix, plen + 1 + slen + (indent + width + 1) * lines); + t = realloc(*prefix, (ssize_t) plen + 1 + slen + (indent + width + 1) * lines); if (!t) return -ENOMEM; @@ -639,7 +640,7 @@ int base64_append( return base64_append_width(prefix, plen, "\n", indent, p, l, width - indent - 1); else /* leave plen on the left, keep last column free */ - return base64_append_width(prefix, plen, NULL, plen, p, l, width - plen - 1); + return base64_append_width(prefix, plen, " ", plen, p, l, width - plen - 1); } static int unbase64_next(const char **p, size_t *l) { @@ -684,11 +685,12 @@ static int unbase64_next(const char **p, size_t *l) { return ret; } -int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { +int unbase64mem_full(const char *p, size_t l, bool secure, void **ret, size_t *ret_size) { _cleanup_free_ uint8_t *buf = NULL; const char *x; uint8_t *z; size_t len; + int r; assert(p || l == 0); assert(ret); @@ -711,36 +713,54 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { a = unbase64_next(&x, &l); if (a == -EPIPE) /* End of string */ break; - if (a < 0) - return a; - if (a == INT_MAX) /* Padding is not allowed at the beginning of a 4ch block */ - return -EINVAL; + if (a < 0) { + r = a; + goto on_failure; + } + if (a == INT_MAX) { /* Padding is not allowed at the beginning of a 4ch block */ + r = -EINVAL; + goto on_failure; + } b = unbase64_next(&x, &l); - if (b < 0) - return b; - if (b == INT_MAX) /* Padding is not allowed at the second character of a 4ch block either */ - return -EINVAL; + if (b < 0) { + r = b; + goto on_failure; + } + if (b == INT_MAX) { /* Padding is not allowed at the second character of a 4ch block either */ + r = -EINVAL; + goto on_failure; + } c = unbase64_next(&x, &l); - if (c < 0) - return c; + if (c < 0) { + r = c; + goto on_failure; + } d = unbase64_next(&x, &l); - if (d < 0) - return d; + if (d < 0) { + r = d; + goto on_failure; + } if (c == INT_MAX) { /* Padding at the third character */ - if (d != INT_MAX) /* If the third character is padding, the fourth must be too */ - return -EINVAL; + if (d != INT_MAX) { /* If the third character is padding, the fourth must be too */ + r = -EINVAL; + goto on_failure; + } /* b == 00YY0000 */ - if (b & 15) - return -EINVAL; + if (b & 15) { + r = -EINVAL; + goto on_failure; + } - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; + if (l > 0) { /* Trailing rubbish? */ + r = -ENAMETOOLONG; + goto on_failure; + } *(z++) = (uint8_t) a << 2 | (uint8_t) (b >> 4); /* XXXXXXYY */ break; @@ -748,11 +768,15 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { if (d == INT_MAX) { /* c == 00ZZZZ00 */ - if (c & 3) - return -EINVAL; + if (c & 3) { + r = -EINVAL; + goto on_failure; + } - if (l > 0) /* Trailing rubbish? */ - return -ENAMETOOLONG; + if (l > 0) { /* Trailing rubbish? */ + r = -ENAMETOOLONG; + goto on_failure; + } *(z++) = (uint8_t) a << 2 | (uint8_t) b >> 4; /* XXXXXXYY */ *(z++) = (uint8_t) b << 4 | (uint8_t) c >> 2; /* YYYYZZZZ */ @@ -770,6 +794,12 @@ int unbase64mem(const char *p, size_t l, void **ret, size_t *ret_size) { *ret = TAKE_PTR(buf); return 0; + +on_failure: + if (secure) + explicit_bzero_safe(buf, len); + + return r; } void hexdump(FILE *f, const void *p, size_t s) { diff --git a/src/basic/hexdecoct.h b/src/basic/hexdecoct.h index 9477d16e3..fa6013ee7 100644 --- a/src/basic/hexdecoct.h +++ b/src/basic/hexdecoct.h @@ -33,6 +33,9 @@ ssize_t base64mem(const void *p, size_t l, char **out); int base64_append(char **prefix, int plen, const void *p, size_t l, int margin, int width); -int unbase64mem(const char *p, size_t l, void **mem, size_t *len); +int unbase64mem_full(const char *p, size_t l, bool secure, void **mem, size_t *len); +static inline int unbase64mem(const char *p, size_t l, void **mem, size_t *len) { + return unbase64mem_full(p, l, false, mem, len); +} void hexdump(FILE *f, const void *p, size_t s); diff --git a/src/basic/in-addr-util.c b/src/basic/in-addr-util.c index 2bffe473c..1d5c6412b 100644 --- a/src/basic/in-addr-util.c +++ b/src/basic/in-addr-util.c @@ -5,12 +5,15 @@ #include #include #include +#include #include #include "alloc-util.h" #include "in-addr-util.h" #include "macro.h" #include "parse-util.h" +#include "random-util.h" +#include "strxcpyx.h" #include "util.h" bool in4_addr_is_null(const struct in_addr *a) { @@ -68,6 +71,14 @@ bool in4_addr_is_localhost(const struct in_addr *a) { return (be32toh(a->s_addr) & UINT32_C(0xFF000000)) == UINT32_C(127) << 24; } +bool in4_addr_is_non_local(const struct in_addr *a) { + /* Whether the address is not null and not localhost. + * + * As such, it is suitable to configure as DNS/NTP server from DHCP. */ + return !in4_addr_is_null(a) && + !in4_addr_is_localhost(a); +} + int in_addr_is_localhost(int family, const union in_addr_union *u) { assert(u); @@ -207,8 +218,85 @@ int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen) return -EAFNOSUPPORT; } +int in_addr_random_prefix( + int family, + union in_addr_union *u, + unsigned prefixlen_fixed_part, + unsigned prefixlen) { + + assert(u); + + /* Random network part of an address by one. */ + + if (prefixlen <= 0) + return 0; + + if (family == AF_INET) { + uint32_t c, n; + + if (prefixlen_fixed_part > 32) + prefixlen_fixed_part = 32; + if (prefixlen > 32) + prefixlen = 32; + if (prefixlen_fixed_part >= prefixlen) + return -EINVAL; + + c = be32toh(u->in.s_addr); + c &= ((UINT32_C(1) << prefixlen_fixed_part) - 1) << (32 - prefixlen_fixed_part); + + random_bytes(&n, sizeof(n)); + n &= ((UINT32_C(1) << (prefixlen - prefixlen_fixed_part)) - 1) << (32 - prefixlen); + + u->in.s_addr = htobe32(n | c); + return 1; + } + + if (family == AF_INET6) { + struct in6_addr n; + unsigned i, j; + + if (prefixlen_fixed_part > 128) + prefixlen_fixed_part = 128; + if (prefixlen > 128) + prefixlen = 128; + if (prefixlen_fixed_part >= prefixlen) + return -EINVAL; + + random_bytes(&n, sizeof(n)); + + for (i = 0; i < 16; i++) { + uint8_t mask_fixed_part = 0, mask = 0; + + if (i < (prefixlen_fixed_part + 7) / 8) { + if (i < prefixlen_fixed_part / 8) + mask_fixed_part = 0xffu; + else { + j = prefixlen_fixed_part % 8; + mask_fixed_part = ((UINT8_C(1) << (j + 1)) - 1) << (8 - j); + } + } + + if (i < (prefixlen + 7) / 8) { + if (i < prefixlen / 8) + mask = 0xffu ^ mask_fixed_part; + else { + j = prefixlen % 8; + mask = (((UINT8_C(1) << (j + 1)) - 1) << (8 - j)) ^ mask_fixed_part; + } + } + + u->in6.s6_addr[i] &= mask_fixed_part; + u->in6.s6_addr[i] |= n.s6_addr[i] & mask; + } + + return 1; + } + + return -EAFNOSUPPORT; +} + int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { - char *x; + _cleanup_free_ char *x = NULL; size_t l; assert(u); @@ -226,18 +314,50 @@ int in_addr_to_string(int family, const union in_addr_union *u, char **ret) { return -ENOMEM; errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); + if (!inet_ntop(family, u, x, l)) return errno > 0 ? -errno : -EINVAL; - } - *ret = x; + *ret = TAKE_PTR(x); + return 0; +} + +int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret) { + _cleanup_free_ char *x = NULL; + char *p; + size_t l; + + assert(u); + assert(ret); + + if (family == AF_INET) + l = INET_ADDRSTRLEN + 3; + else if (family == AF_INET6) + l = INET6_ADDRSTRLEN + 4; + else + return -EAFNOSUPPORT; + + if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8) + return -EINVAL; + + x = new(char, l); + if (!x) + return -ENOMEM; + + errno = 0; + if (!inet_ntop(family, u, x, l)) + return errno > 0 ? -errno : -EINVAL; + + p = x + strlen(x); + l -= strlen(x); + (void) strpcpyf(&p, l, "/%u", prefixlen); + + *ret = TAKE_PTR(x); return 0; } int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret) { + _cleanup_free_ char *x = NULL; size_t l; - char *x; int r; assert(u); @@ -263,14 +383,12 @@ int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifin return -ENOMEM; errno = 0; - if (!inet_ntop(family, u, x, l)) { - free(x); + if (!inet_ntop(family, u, x, l)) return errno > 0 ? -errno : -EINVAL; - } sprintf(strchr(x, 0), "%%%i", ifindex); - *ret = x; + *ret = TAKE_PTR(x); return 0; fallback: diff --git a/src/basic/in-addr-util.h b/src/basic/in-addr-util.h index 306979051..a6a685b91 100644 --- a/src/basic/in-addr-util.h +++ b/src/basic/in-addr-util.h @@ -30,10 +30,14 @@ int in_addr_is_link_local(int family, const union in_addr_union *u); bool in4_addr_is_localhost(const struct in_addr *a); int in_addr_is_localhost(int family, const union in_addr_union *u); +bool in4_addr_is_non_local(const struct in_addr *a); + int in_addr_equal(int family, const union in_addr_union *a, const union in_addr_union *b); int in_addr_prefix_intersect(int family, const union in_addr_union *a, unsigned aprefixlen, const union in_addr_union *b, unsigned bprefixlen); int in_addr_prefix_next(int family, union in_addr_union *u, unsigned prefixlen); +int in_addr_random_prefix(int family, union in_addr_union *u, unsigned prefixlen_fixed_part, unsigned prefixlen); int in_addr_to_string(int family, const union in_addr_union *u, char **ret); +int in_addr_prefix_to_string(int family, const union in_addr_union *u, unsigned prefixlen, char **ret); int in_addr_ifindex_to_string(int family, const union in_addr_union *u, int ifindex, char **ret); int in_addr_from_string(int family, const char *s, union in_addr_union *ret); int in_addr_from_string_auto(const char *s, int *ret_family, union in_addr_union *ret); diff --git a/src/basic/kbd-util.c b/src/basic/kbd-util.c new file mode 100644 index 000000000..17d4022db --- /dev/null +++ b/src/basic/kbd-util.c @@ -0,0 +1,109 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "kbd-util.h" +#include "log.h" +#include "nulstr-util.h" +#include "path-util.h" +#include "set.h" +#include "string-util.h" +#include "strv.h" +#include "utf8.h" + +static thread_local Set *keymaps = NULL; + +static int nftw_cb( + const char *fpath, + const struct stat *sb, + int tflag, + struct FTW *ftwbuf) { + + _cleanup_free_ char *p = NULL; + char *e; + int r; + + if (tflag != FTW_F) + return 0; + + if (!endswith(fpath, ".map") && + !endswith(fpath, ".map.gz")) + return 0; + + p = strdup(basename(fpath)); + if (!p) + return FTW_STOP; + + e = endswith(p, ".map"); + if (e) + *e = 0; + + e = endswith(p, ".map.gz"); + if (e) + *e = 0; + + if (!keymap_is_valid(p)) + return 0; + + r = set_consume(keymaps, TAKE_PTR(p)); + if (r < 0 && r != -EEXIST) + return r; + + return 0; +} + +int get_keymaps(char ***ret) { + _cleanup_strv_free_ char **l = NULL; + const char *dir; + int r; + + keymaps = set_new(&string_hash_ops); + if (!keymaps) + return -ENOMEM; + + NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { + r = nftw(dir, nftw_cb, 20, FTW_PHYS|FTW_ACTIONRETVAL); + + if (r == FTW_STOP) + log_debug("Directory not found %s", dir); + else if (r < 0) + log_debug_errno(r, "Can't add keymap: %m"); + } + + l = set_get_strv(keymaps); + if (!l) { + set_free_free(keymaps); + return -ENOMEM; + } + + set_free(keymaps); + + if (strv_isempty(l)) + return -ENOENT; + + strv_sort(l); + + *ret = TAKE_PTR(l); + + return 0; +} + +bool keymap_is_valid(const char *name) { + + if (isempty(name)) + return false; + + if (strlen(name) >= 128) + return false; + + if (!utf8_is_valid(name)) + return false; + + if (!filename_is_valid(name)) + return false; + + if (!string_is_safe(name)) + return false; + + return true; +} diff --git a/src/basic/kbd-util.h b/src/basic/kbd-util.h new file mode 100644 index 000000000..9efd2c705 --- /dev/null +++ b/src/basic/kbd-util.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#if HAVE_SPLIT_USR +#define KBD_KEYMAP_DIRS \ + "/usr/share/keymaps/\0" \ + "/usr/share/kbd/keymaps/\0" \ + "/usr/lib/kbd/keymaps/\0" \ + "/lib/kbd/keymaps/\0" +#else +#define KBD_KEYMAP_DIRS \ + "/usr/share/keymaps/\0" \ + "/usr/share/kbd/keymaps/\0" \ + "/usr/lib/kbd/keymaps/\0" +#endif + +int get_keymaps(char ***l); +bool keymap_is_valid(const char *name); diff --git a/src/basic/limits-util.c b/src/basic/limits-util.c new file mode 100644 index 000000000..fbf52e55f --- /dev/null +++ b/src/basic/limits-util.c @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "cgroup-util.h" +#include "limits-util.h" +#include "memory-util.h" +#include "parse-util.h" +#include "process-util.h" +#include "procfs-util.h" +#include "string-util.h" + +uint64_t physical_memory(void) { + _cleanup_free_ char *root = NULL, *value = NULL; + uint64_t mem, lim; + size_t ps; + long sc; + int r; + + /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of + * memory. + * + * In order to support containers nicely that have a configured memory limit we'll take the minimum of the + * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ + + sc = sysconf(_SC_PHYS_PAGES); + assert(sc > 0); + + ps = page_size(); + mem = (uint64_t) sc * (uint64_t) ps; + + r = cg_get_root_path(&root); + if (r < 0) { + log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); + return mem; + } + + r = cg_all_unified(); + if (r < 0) { + log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); + return mem; + } + if (r > 0) { + r = cg_get_attribute("memory", root, "memory.max", &value); + if (r < 0) { + log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); + return mem; + } + + if (streq(value, "max")) + return mem; + } else { + r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); + if (r < 0) { + log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); + return mem; + } + } + + r = safe_atou64(value, &lim); + if (r < 0) { + log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); + return mem; + } + if (lim == UINT64_MAX) + return mem; + + /* Make sure the limit is a multiple of our own page size */ + lim /= ps; + lim *= ps; + + return MIN(mem, lim); +} + +uint64_t physical_memory_scale(uint64_t v, uint64_t max) { + uint64_t p, m, ps, r; + + assert(max > 0); + + /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success + * the result is a multiple of the page size (rounds down). */ + + ps = page_size(); + assert(ps > 0); + + p = physical_memory() / ps; + assert(p > 0); + + m = p * v; + if (m / p != v) + return UINT64_MAX; + + m /= max; + + r = m * ps; + if (r / ps != m) + return UINT64_MAX; + + return r; +} + +uint64_t system_tasks_max(void) { + + uint64_t a = TASKS_MAX, b = TASKS_MAX; + _cleanup_free_ char *root = NULL; + int r; + + /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this + * limit: + * + * a) the maximum tasks value the kernel allows on this architecture + * b) the cgroups pids_max attribute for the system + * c) the kernel's configured maximum PID value + * + * And then pick the smallest of the three */ + + r = procfs_tasks_get_limit(&a); + if (r < 0) + log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); + + r = cg_get_root_path(&root); + if (r < 0) + log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); + else { + _cleanup_free_ char *value = NULL; + + r = cg_get_attribute("pids", root, "pids.max", &value); + if (r < 0) + log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); + else if (!streq(value, "max")) { + r = safe_atou64(value, &b); + if (r < 0) + log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); + } + } + + return MIN3(TASKS_MAX, + a <= 0 ? TASKS_MAX : a, + b <= 0 ? TASKS_MAX : b); +} + +uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { + uint64_t t, m; + + assert(max > 0); + + /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages + * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ + + t = system_tasks_max(); + assert(t > 0); + + m = t * v; + if (m / t != v) /* overflow? */ + return UINT64_MAX; + + return m / max; +} diff --git a/src/basic/limits-util.h b/src/basic/limits-util.h new file mode 100644 index 000000000..77895cbad --- /dev/null +++ b/src/basic/limits-util.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +uint64_t physical_memory(void); +uint64_t physical_memory_scale(uint64_t v, uint64_t max); + +uint64_t system_tasks_max(void); +uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); diff --git a/src/basic/locale-util.c b/src/basic/locale-util.c index fc1577a83..6f41f5064 100644 --- a/src/basic/locale-util.c +++ b/src/basic/locale-util.c @@ -255,99 +255,6 @@ out: return (bool) cached_answer; } -static thread_local Set *keymaps = NULL; - -static int nftw_cb( - const char *fpath, - const struct stat *sb, - int tflag, - struct FTW *ftwbuf) { - - char *p, *e; - int r; - - if (tflag != FTW_F) - return 0; - - if (!endswith(fpath, ".map") && - !endswith(fpath, ".map.gz")) - return 0; - - p = strdup(basename(fpath)); - if (!p) - return FTW_STOP; - - e = endswith(p, ".map"); - if (e) - *e = 0; - - e = endswith(p, ".map.gz"); - if (e) - *e = 0; - - r = set_consume(keymaps, p); - if (r < 0 && r != -EEXIST) - return r; - - return 0; -} - -int get_keymaps(char ***ret) { - _cleanup_strv_free_ char **l = NULL; - const char *dir; - int r; - - keymaps = set_new(&string_hash_ops); - if (!keymaps) - return -ENOMEM; - - NULSTR_FOREACH(dir, KBD_KEYMAP_DIRS) { - r = nftw(dir, nftw_cb, 20, FTW_PHYS|FTW_ACTIONRETVAL); - - if (r == FTW_STOP) - log_debug("Directory not found %s", dir); - else if (r < 0) - log_debug_errno(r, "Can't add keymap: %m"); - } - - l = set_get_strv(keymaps); - if (!l) { - set_free_free(keymaps); - return -ENOMEM; - } - - set_free(keymaps); - - if (strv_isempty(l)) - return -ENOENT; - - strv_sort(l); - - *ret = TAKE_PTR(l); - - return 0; -} - -bool keymap_is_valid(const char *name) { - - if (isempty(name)) - return false; - - if (strlen(name) >= 128) - return false; - - if (!utf8_is_valid(name)) - return false; - - if (!filename_is_valid(name)) - return false; - - if (!string_is_safe(name)) - return false; - - return true; -} - static bool emoji_enabled(void) { static int cached_emoji_enabled = -1; diff --git a/src/basic/locale-util.h b/src/basic/locale-util.h index e64f0ce15..78abbafd8 100644 --- a/src/basic/locale-util.h +++ b/src/basic/locale-util.h @@ -68,9 +68,6 @@ const char *special_glyph(SpecialGlyph code) _const_; const char* locale_variable_to_string(LocaleVariable i) _const_; LocaleVariable locale_variable_from_string(const char *s) _pure_; -int get_keymaps(char ***l); -bool keymap_is_valid(const char *name); - static inline void freelocalep(locale_t *p) { if (*p == (locale_t) 0) return; diff --git a/src/basic/log.c b/src/basic/log.c index 048602729..ea252c413 100644 --- a/src/basic/log.c +++ b/src/basic/log.c @@ -19,6 +19,7 @@ #include "sd-messages.h" #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "format-util.h" #include "io-util.h" @@ -37,7 +38,6 @@ #include "terminal-util.h" #include "time-util.h" #include "utf8.h" -#include "util.h" #define SNDBUF_SIZE (8*1024*1024) @@ -699,8 +699,7 @@ int log_internal_realm( return r; } -_printf_(10,0) -static int log_object_internalv( +int log_object_internalv( int level, int error, const char *file, @@ -1223,7 +1222,7 @@ int log_syntax_internal( log_target == LOG_TARGET_NULL) return -ERRNO_VALUE(error); - errno = error; + errno = ERRNO_VALUE(error); va_start(ap, format); (void) vsnprintf(buffer, sizeof buffer, format, ap); @@ -1232,16 +1231,34 @@ int log_syntax_internal( if (unit) unit_fmt = getpid_cached() == 1 ? "UNIT=%s" : "USER_UNIT=%s"; - 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", config_file, - "CONFIG_LINE=%u", config_line, - LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer), - unit_fmt, unit, - NULL); + if (config_file) + 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", config_file, + "CONFIG_LINE=%u", config_line, + LOG_MESSAGE("%s:%u: %s", config_file, config_line, buffer), + unit_fmt, unit, + NULL); + else if (unit) + 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: %s", unit, buffer), + unit_fmt, unit, + 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 log_syntax_invalid_utf8_internal( diff --git a/src/basic/log.h b/src/basic/log.h index 17438d7ff..aa3d5b75f 100644 --- a/src/basic/log.h +++ b/src/basic/log.h @@ -115,6 +115,19 @@ int log_internalv_realm( log_internalv_realm(LOG_REALM_PLUS_LEVEL(LOG_REALM, (level)), __VA_ARGS__) /* Realm is fixed to LOG_REALM_SYSTEMD for those */ +int log_object_internalv( + int level, + int error, + const char *file, + int line, + const char *func, + const char *object_field, + const char *object, + const char *extra_field, + const char *extra, + const char *format, + va_list ap) _printf_(10,0); + int log_object_internal( int level, int error, @@ -308,7 +321,7 @@ int log_syntax_invalid_utf8_internal( int _level = (level), _e = (error); \ (log_get_max_level() >= LOG_PRI(_level)) \ ? log_syntax_internal(unit, _level, config_file, config_line, _e, __FILE__, __LINE__, __func__, __VA_ARGS__) \ - : -abs(_e); \ + : -ERRNO_VALUE(_e); \ }) #define log_syntax_invalid_utf8(unit, level, config_file, config_line, rvalue) \ diff --git a/src/basic/memory-util.c b/src/basic/memory-util.c new file mode 100644 index 000000000..5f327ef0d --- /dev/null +++ b/src/basic/memory-util.c @@ -0,0 +1,57 @@ +#include + +#include "memory-util.h" + +size_t page_size(void) { + static thread_local size_t pgsz = 0; + long r; + + if (_likely_(pgsz > 0)) + return pgsz; + + r = sysconf(_SC_PAGESIZE); + assert(r > 0); + + pgsz = (size_t) r; + return pgsz; +} + +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; +} + +#if !HAVE_EXPLICIT_BZERO +/* + * The pointer to memset() is volatile so that compiler must de-reference the pointer and can't assume that + * it points to any function in particular (such as memset(), which it then might further "optimize"). This + * approach is inspired by openssl's crypto/mem_clr.c. + */ +typedef void *(*memset_t)(void *,int,size_t); + +static volatile memset_t memset_func = memset; + +void* explicit_bzero_safe(void *p, size_t l) { + if (l > 0) + memset_func(p, '\0', l); + + return p; +} +#endif diff --git a/src/basic/memory-util.h b/src/basic/memory-util.h new file mode 100644 index 000000000..915c24a5d --- /dev/null +++ b/src/basic/memory-util.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include +#include +#include + +#include "macro.h" + +size_t page_size(void) _pure_; +#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) + +/* 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; + assert(src); + 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); +} + +#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; +} + +/* 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) { + + if (needlelen <= 0) + return (void*) haystack; + + if (haystacklen < needlelen) + return NULL; + + assert(haystack); + assert(needle); + + return memmem(haystack, haystacklen, needle, needlelen); +} + +#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 + +/* Use with _cleanup_ to erase a single 'char' when leaving scope */ +static inline void erase_char(char *p) { + explicit_bzero_safe(p, sizeof(char)); +} diff --git a/src/basic/mempool.c b/src/basic/mempool.c index 159c96337..22df42105 100644 --- a/src/basic/mempool.c +++ b/src/basic/mempool.c @@ -5,6 +5,7 @@ #include "env-util.h" #include "macro.h" +#include "memory-util.h" #include "mempool.h" #include "process-util.h" #include "util.h" diff --git a/src/basic/meson.build b/src/basic/meson.build index e5852f32f..91e0df3d2 100644 --- a/src/basic/meson.build +++ b/src/basic/meson.build @@ -45,6 +45,7 @@ basic_sources = files(''' env-util.h errno-list.c errno-list.h + errno-util.h escape.c escape.h ether-addr-util.c @@ -75,10 +76,14 @@ basic_sources = files(''' io-util.c io-util.h ioprio.h + kbd-util.c + kbd-util.h khash.c khash.h label.c label.h + limits-util.c + limits-util.h list.h locale-util.c locale-util.h @@ -89,6 +94,8 @@ basic_sources = files(''' macro.h memfd-util.c memfd-util.h + memory-util.c + memory-util.h mempool.c mempool.h missing.h @@ -127,13 +134,19 @@ basic_sources = files(''' mkdir.h mountpoint-util.c mountpoint-util.h + namespace-util.c + namespace-util.h nss-util.h + nulstr-util.c + nulstr-util.h ordered-set.c ordered-set.h parse-util.c parse-util.h path-util.c path-util.h + plymouth-util.c + plymouth-util.h prioq.c prioq.h proc-cmdline.c @@ -148,7 +161,6 @@ basic_sources = files(''' ratelimit.h raw-clone.h raw-reboot.h - refcnt.h replace-var.c replace-var.h rlimit-util.c @@ -169,6 +181,8 @@ basic_sources = files(''' socket-label.c socket-util.c socket-util.h + sort-util.c + sort-util.h sparse-endian.h special.h stat-util.c diff --git a/src/basic/missing_fs.h b/src/basic/missing_fs.h index 48c1af045..c262a9181 100644 --- a/src/basic/missing_fs.h +++ b/src/basic/missing_fs.h @@ -61,3 +61,7 @@ #ifndef NS_GET_NSTYPE /* d95fa3c76a66b6d76b1e109ea505c55e66360f3c (4.11) */ #define NS_GET_NSTYPE _IO(0xb7, 0x3) #endif + +#ifndef FS_PROJINHERIT_FL +#define FS_PROJINHERIT_FL 0x20000000 +#endif diff --git a/src/basic/missing_network.h b/src/basic/missing_network.h index 59a8cd2c6..80ef13fd3 100644 --- a/src/basic/missing_network.h +++ b/src/basic/missing_network.h @@ -98,10 +98,22 @@ #endif /* netdevice.h */ +#ifndef NET_ADDR_PERM +#define NET_ADDR_PERM 0 +#endif + #ifndef NET_ADDR_RANDOM #define NET_ADDR_RANDOM 1 #endif +#ifndef NET_ADDR_STOLEN +#define NET_ADDR_STOLEN 2 +#endif + +#ifndef NET_ADDR_SET +#define NET_ADDR_SET 3 +#endif + #ifndef NET_NAME_UNKNOWN #define NET_NAME_UNKNOWN 0 #endif diff --git a/src/basic/missing_socket.h b/src/basic/missing_socket.h index a5fd45724..276be366c 100644 --- a/src/basic/missing_socket.h +++ b/src/basic/missing_socket.h @@ -32,6 +32,10 @@ struct sockaddr_vm { #define SO_PEERGROUPS 59 #endif +#ifndef SO_BINDTOIFINDEX +#define SO_BINDTOIFINDEX 62 +#endif + #ifndef SOL_NETLINK #define SOL_NETLINK 270 #endif diff --git a/src/basic/mkdir.c b/src/basic/mkdir.c index 4bb65d583..6b82eab64 100644 --- a/src/basic/mkdir.c +++ b/src/basic/mkdir.c @@ -6,6 +6,7 @@ #include #include "alloc-util.h" +#include "format-util.h" #include "fs-util.h" #include "macro.h" #include "mkdir.h" diff --git a/src/basic/namespace-util.c b/src/basic/namespace-util.c new file mode 100644 index 000000000..67bdaa19d --- /dev/null +++ b/src/basic/namespace-util.c @@ -0,0 +1,172 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include + +#include "fd-util.h" +#include "missing.h" +#include "namespace-util.h" +#include "process-util.h" +#include "stat-util.h" +#include "user-util.h" + +int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { + _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; + int rfd = -1; + + assert(pid >= 0); + + if (mntns_fd) { + const char *mntns; + + mntns = procfs_file_alloca(pid, "ns/mnt"); + mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (mntnsfd < 0) + return -errno; + } + + if (pidns_fd) { + const char *pidns; + + pidns = procfs_file_alloca(pid, "ns/pid"); + pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (pidnsfd < 0) + return -errno; + } + + if (netns_fd) { + const char *netns; + + netns = procfs_file_alloca(pid, "ns/net"); + netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (netnsfd < 0) + return -errno; + } + + if (userns_fd) { + const char *userns; + + userns = procfs_file_alloca(pid, "ns/user"); + usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (usernsfd < 0 && errno != ENOENT) + return -errno; + } + + if (root_fd) { + const char *root; + + root = procfs_file_alloca(pid, "root"); + rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + if (rfd < 0) + return -errno; + } + + if (pidns_fd) + *pidns_fd = pidnsfd; + + if (mntns_fd) + *mntns_fd = mntnsfd; + + if (netns_fd) + *netns_fd = netnsfd; + + if (userns_fd) + *userns_fd = usernsfd; + + if (root_fd) + *root_fd = rfd; + + pidnsfd = mntnsfd = netnsfd = usernsfd = -1; + + return 0; +} + +int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { + if (userns_fd >= 0) { + /* Can't setns to your own userns, since then you could + * escalate from non-root to root in your own namespace, so + * check if namespaces equal before attempting to enter. */ + _cleanup_free_ char *userns_fd_path = NULL; + int r; + if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) + return -ENOMEM; + + r = files_same(userns_fd_path, "/proc/self/ns/user", 0); + if (r < 0) + return r; + if (r) + userns_fd = -1; + } + + if (pidns_fd >= 0) + if (setns(pidns_fd, CLONE_NEWPID) < 0) + return -errno; + + if (mntns_fd >= 0) + if (setns(mntns_fd, CLONE_NEWNS) < 0) + return -errno; + + if (netns_fd >= 0) + if (setns(netns_fd, CLONE_NEWNET) < 0) + return -errno; + + if (userns_fd >= 0) + if (setns(userns_fd, CLONE_NEWUSER) < 0) + return -errno; + + if (root_fd >= 0) { + if (fchdir(root_fd) < 0) + return -errno; + + if (chroot(".") < 0) + return -errno; + } + + return reset_uid_gid(); +} + +int fd_is_network_ns(int fd) { + struct statfs s; + int 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 (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; +} diff --git a/src/basic/namespace-util.h b/src/basic/namespace-util.h new file mode 100644 index 000000000..8c17ce91b --- /dev/null +++ b/src/basic/namespace-util.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); +int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); + +int fd_is_network_ns(int fd); diff --git a/src/basic/nulstr-util.c b/src/basic/nulstr-util.c new file mode 100644 index 000000000..b12d02023 --- /dev/null +++ b/src/basic/nulstr-util.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "nulstr-util.h" +#include "string-util.h" + +bool nulstr_contains(const char *nulstr, const char *needle) { + const char *i; + + if (!nulstr) + return false; + + NULSTR_FOREACH(i, nulstr) + if (streq(i, needle)) + return true; + + return false; +} diff --git a/src/basic/nulstr-util.h b/src/basic/nulstr-util.h new file mode 100644 index 000000000..436b271f3 --- /dev/null +++ b/src/basic/nulstr-util.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +#define NULSTR_FOREACH(i, l) \ + for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) + +#define NULSTR_FOREACH_PAIR(i, j, l) \ + for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) + +bool nulstr_contains(const char *nulstr, const char *needle); diff --git a/src/basic/ordered-set.c b/src/basic/ordered-set.c index ed9ba776a..7fdb47e06 100644 --- a/src/basic/ordered-set.c +++ b/src/basic/ordered-set.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include "fileio.h" #include "ordered-set.h" #include "strv.h" @@ -45,3 +46,37 @@ int ordered_set_put_strdupv(OrderedSet *s, char **l) { return n; } + +int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l) { + int n = 0, r; + Iterator i; + char *p; + + /* Like ordered_set_put_strv, but for an OrderedSet of strings */ + + ORDERED_SET_FOREACH(p, l, i) { + r = ordered_set_put_strdup(s, p); + if (r < 0) + return r; + + n += r; + } + + return n; +} + +void ordered_set_print(FILE *f, const char *field, OrderedSet *s) { + bool space = false; + Iterator i; + char *p; + + if (ordered_set_isempty(s)) + return; + + fputs(field, f); + + ORDERED_SET_FOREACH(p, s, i) + fputs_with_space(f, p, NULL, &space); + + fputc('\n', f); +} diff --git a/src/basic/ordered-set.h b/src/basic/ordered-set.h index 7cbb71819..61107eb14 100644 --- a/src/basic/ordered-set.h +++ b/src/basic/ordered-set.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + #include "hashmap.h" typedef struct OrderedSet OrderedSet; @@ -48,9 +50,15 @@ static inline void* ordered_set_steal_first(OrderedSet *s) { return ordered_hashmap_steal_first((OrderedHashmap*) s); } +static inline char **ordered_set_get_strv(OrderedSet *s) { + return internal_hashmap_get_strv(HASHMAP_BASE((OrderedHashmap*) s)); +} + int ordered_set_consume(OrderedSet *s, void *p); int ordered_set_put_strdup(OrderedSet *s, const char *p); int ordered_set_put_strdupv(OrderedSet *s, char **l); +int ordered_set_put_string_set(OrderedSet *s, OrderedSet *l); +void ordered_set_print(FILE *f, const char *field, OrderedSet *s); #define ORDERED_SET_FOREACH(e, s, i) \ for ((i) = ITERATOR_FIRST; ordered_set_iterate((s), &(i), (void**)&(e)); ) diff --git a/src/basic/path-util.c b/src/basic/path-util.c index 221517303..55cb140d8 100644 --- a/src/basic/path-util.c +++ b/src/basic/path-util.c @@ -21,6 +21,7 @@ #include "log.h" #include "macro.h" #include "missing.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "stat-util.h" @@ -1103,48 +1104,40 @@ int path_simplify_and_warn( unsigned line, const char *lvalue) { - bool absolute, fatal = flag & PATH_CHECK_FATAL; + bool fatal = flag & PATH_CHECK_FATAL; assert(!FLAGS_SET(flag, PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)); - if (!utf8_is_valid(path)) { - log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); - return -EINVAL; - } + if (!utf8_is_valid(path)) + return log_syntax_invalid_utf8(unit, LOG_ERR, filename, line, path); if (flag & (PATH_CHECK_ABSOLUTE | PATH_CHECK_RELATIVE)) { + bool absolute; + absolute = path_is_absolute(path); - if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } + if (!absolute && (flag & PATH_CHECK_ABSOLUTE)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path is not absolute%s: %s", + lvalue, fatal ? "" : ", ignoring", path); - if (absolute && (flag & PATH_CHECK_RELATIVE)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is absolute%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } + if (absolute && (flag & PATH_CHECK_RELATIVE)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path is absolute%s: %s", + lvalue, fatal ? "" : ", ignoring", path); } path_simplify(path, true); - if (!path_is_normalized(path)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "%s= path is not normalized%s: %s", - lvalue, fatal ? "" : ", ignoring", path); - return -EINVAL; - } + if (!path_is_valid(path)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path has invalid length (%zu bytes)%s.", + lvalue, strlen(path), fatal ? "" : ", ignoring"); - 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; - } + if (!path_is_normalized(path)) + return log_syntax(unit, LOG_ERR, filename, line, SYNTHETIC_ERRNO(EINVAL), + "%s= path is not normalized%s: %s", + lvalue, fatal ? "" : ", ignoring", path); return 0; } diff --git a/src/basic/plymouth-util.c b/src/basic/plymouth-util.c new file mode 100644 index 000000000..2023ec367 --- /dev/null +++ b/src/basic/plymouth-util.c @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "plymouth-util.h" + +bool plymouth_running(void) { + return access("/run/plymouth/pid", F_OK) >= 0; +} diff --git a/src/basic/plymouth-util.h b/src/basic/plymouth-util.h new file mode 100644 index 000000000..e0539910c --- /dev/null +++ b/src/basic/plymouth-util.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#define PLYMOUTH_SOCKET { \ + .un.sun_family = AF_UNIX, \ + .un.sun_path = "\0/org/freedesktop/plymouthd", \ + } + +bool plymouth_running(void); diff --git a/src/basic/process-util.c b/src/basic/process-util.c index 78ce43b94..f773eeaff 100644 --- a/src/basic/process-util.c +++ b/src/basic/process-util.c @@ -32,7 +32,9 @@ #include "ioprio.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" +#include "namespace-util.h" #include "process-util.h" #include "raw-clone.h" #include "rlimit-util.h" @@ -42,7 +44,6 @@ #include "string-util.h" #include "terminal-util.h" #include "user-util.h" -#include "util.h" int get_process_state(pid_t pid) { const char *p; @@ -933,6 +934,20 @@ int getenv_for_pid(pid_t pid, const char *field, char **ret) { return 0; } +int pid_is_my_child(pid_t pid) { + pid_t ppid; + int r; + + if (pid <= 1) + return false; + + r = get_process_ppid(pid, &ppid); + if (r < 0) + return r; + + return ppid == getpid_cached(); +} + bool pid_is_unwaited(pid_t pid) { /* Checks whether a PID is still valid at all, including a zombie */ @@ -1000,7 +1015,7 @@ _noreturn_ void freeze(void) { log_close(); /* Make sure nobody waits for us on a socket anymore */ - close_all_fds(NULL, 0); + (void) close_all_fds(NULL, 0); sync(); @@ -1534,6 +1549,40 @@ int set_oom_score_adjust(int value) { WRITE_STRING_FILE_VERIFY_ON_FAILURE|WRITE_STRING_FILE_DISABLE_BUFFER); } +int cpus_in_affinity_mask(void) { + size_t n = 16; + int r; + + for (;;) { + cpu_set_t *c; + + c = CPU_ALLOC(n); + if (!c) + return -ENOMEM; + + if (sched_getaffinity(0, CPU_ALLOC_SIZE(n), c) >= 0) { + int k; + + k = CPU_COUNT_S(CPU_ALLOC_SIZE(n), c); + CPU_FREE(c); + + if (k <= 0) + return -EINVAL; + + return k; + } + + r = -errno; + CPU_FREE(c); + + if (r != -EINVAL) + return r; + if (n > SIZE_MAX/2) + return -ENOMEM; + n *= 2; + } +} + static const char *const ioprio_class_table[] = { [IOPRIO_CLASS_NONE] = "none", [IOPRIO_CLASS_RT] = "realtime", diff --git a/src/basic/process-util.h b/src/basic/process-util.h index c85ea30ec..7e1d65a7b 100644 --- a/src/basic/process-util.h +++ b/src/basic/process-util.h @@ -12,6 +12,7 @@ #include #include +#include "alloc-util.h" #include "format-util.h" #include "ioprio.h" #include "macro.h" @@ -68,6 +69,7 @@ int getenv_for_pid(pid_t pid, const char *field, char **_value); bool pid_is_alive(pid_t pid); bool pid_is_unwaited(pid_t pid); +int pid_is_my_child(pid_t pid); int pid_from_same_root_fs(pid_t pid); bool is_main_thread(void); @@ -192,3 +194,5 @@ assert_cc(TASKS_MAX <= (unsigned long) PID_T_MAX) (pid) = 0; \ _pid_; \ }) + +int cpus_in_affinity_mask(void); diff --git a/src/basic/random-util.c b/src/basic/random-util.c index f7decf60b..ca25fd242 100644 --- a/src/basic/random-util.c +++ b/src/basic/random-util.c @@ -23,16 +23,13 @@ # include #endif +#include "alloc-util.h" #include "fd-util.h" #include "io-util.h" #include "missing.h" #include "random-util.h" #include "time-util.h" -#if HAS_FEATURE_MEMORY_SANITIZER -#include -#endif - int rdrand(unsigned long *ret) { #if defined(__i386__) || defined(__x86_64__) @@ -58,11 +55,7 @@ int rdrand(unsigned long *ret) { "setc %1" : "=r" (*ret), "=qm" (err)); - -#if HAS_FEATURE_MEMORY_SANITIZER - __msan_unpoison(&err, sizeof(err)); -#endif - + msan_unpoison(&err, sizeof(err)); if (!err) return -EAGAIN; diff --git a/src/basic/refcnt.h b/src/basic/refcnt.h deleted file mode 100644 index 40f9a84a2..000000000 --- a/src/basic/refcnt.h +++ /dev/null @@ -1,54 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1+ */ -#pragma once - -/* A type-safe atomic refcounter. - * - * DO NOT USE THIS UNLESS YOU ACTUALLY CARE ABOUT THREAD SAFETY! */ - -typedef struct { - volatile unsigned _value; -} RefCount; - -#define REFCNT_GET(r) ((r)._value) -#define REFCNT_INC(r) (__sync_add_and_fetch(&(r)._value, 1)) -#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/rm-rf.c b/src/basic/rm-rf.c index 0c957c9b3..acc0f88cd 100644 --- a/src/basic/rm-rf.c +++ b/src/basic/rm-rf.c @@ -165,6 +165,11 @@ int rm_rf(const char *path, RemoveFlags flags) { assert(path); + /* For now, don't support dropping subvols when also only dropping directories, since we can't do + * this race-freely. */ + if (FLAGS_SET(flags, REMOVE_ONLY_DIRECTORIES|REMOVE_SUBVOLUME)) + return -EINVAL; + /* We refuse to clean the root file system with this * call. This is extra paranoia to never cause a really * seriously broken system. */ diff --git a/src/basic/rm-rf.h b/src/basic/rm-rf.h index 3ee2b97e3..d42ebef43 100644 --- a/src/basic/rm-rf.h +++ b/src/basic/rm-rf.h @@ -3,7 +3,7 @@ #include -#include "util.h" +#include "errno-util.h" typedef enum RemoveFlags { REMOVE_ONLY_DIRECTORIES = 1 << 0, diff --git a/src/basic/selinux-util.c b/src/basic/selinux-util.c index dc06f3d07..f35e76023 100644 --- a/src/basic/selinux-util.c +++ b/src/basic/selinux-util.c @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include #include #include #include +#include #include #include @@ -16,6 +18,7 @@ #endif #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "log.h" #include "macro.h" @@ -23,7 +26,6 @@ #include "selinux-util.h" #include "stdio-util.h" #include "time-util.h" -#include "util.h" #if HAVE_SELINUX DEFINE_TRIVIAL_CLEANUP_FUNC(char*, freecon); diff --git a/src/basic/sigbus.c b/src/basic/sigbus.c index d5254eab9..a065a45a8 100644 --- a/src/basic/sigbus.c +++ b/src/basic/sigbus.c @@ -6,8 +6,8 @@ #include #include "macro.h" +#include "memory-util.h" #include "sigbus.h" -#include "util.h" #define SIGBUS_QUEUE_MAX 64 diff --git a/src/basic/socket-label.c b/src/basic/socket-label.c index 4ed19cd93..b5e9b374c 100644 --- a/src/basic/socket-label.c +++ b/src/basic/socket-label.c @@ -68,9 +68,11 @@ int socket_address_listen( } if (IN_SET(socket_address_family(a), AF_INET, AF_INET6)) { - if (bind_to_device) - if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, bind_to_device, strlen(bind_to_device)+1) < 0) - return -errno; + if (bind_to_device) { + r = socket_bind_to_ifname(fd, bind_to_device); + if (r < 0) + return r; + } if (reuse_port) { r = setsockopt_int(fd, SOL_SOCKET, SO_REUSEPORT, true); diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index 91bf801cd..904bafb76 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -15,12 +15,14 @@ #include #include "alloc-util.h" +#include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "parse-util.h" #include "path-util.h" @@ -31,7 +33,6 @@ #include "strv.h" #include "user-util.h" #include "utf8.h" -#include "util.h" #if ENABLE_IDN # define IDN_FLAGS NI_IDN @@ -235,23 +236,32 @@ int socket_address_parse_and_warn(SocketAddress *a, const char *s) { } int socket_address_parse_netlink(SocketAddress *a, const char *s) { - int family; + _cleanup_free_ char *word = NULL; unsigned group = 0; - _cleanup_free_ char *sfamily = NULL; + int family, r; + assert(a); assert(s); zero(*a); a->type = SOCK_RAW; - errno = 0; - if (sscanf(s, "%ms %u", &sfamily, &group) < 1) - return errno > 0 ? -errno : -EINVAL; + r = extract_first_word(&s, &word, NULL, 0); + if (r < 0) + return r; + if (r == 0) + return -EINVAL; - family = netlink_family_from_string(sfamily); + family = netlink_family_from_string(word); if (family < 0) return -EINVAL; + if (!isempty(s)) { + r = safe_atou(s, &group); + if (r < 0) + return r; + } + a->sockaddr.nl.nl_family = AF_NETLINK; a->sockaddr.nl.nl_groups = group; @@ -1228,22 +1238,22 @@ int flush_accept(int fd) { continue; return -errno; - - } else if (r == 0) + } + if (r == 0) return 0; cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (cfd < 0) { - if (errno == EINTR) - continue; - if (errno == EAGAIN) return 0; + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + continue; + return -errno; } - close(cfd); + safe_close(cfd); } } @@ -1345,3 +1355,39 @@ int sockaddr_un_set_path(struct sockaddr_un *ret, const char *path) { return (int) (offsetof(struct sockaddr_un, sun_path) + l + 1); /* include trailing NUL in size */ } } + +int socket_bind_to_ifname(int fd, const char *ifname) { + assert(fd >= 0); + + /* Call with NULL to drop binding */ + + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen_ptr(ifname)) < 0) + return -errno; + + return 0; +} + +int socket_bind_to_ifindex(int fd, int ifindex) { + char ifname[IFNAMSIZ] = ""; + + assert(fd >= 0); + + if (ifindex <= 0) { + /* Drop binding */ + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, NULL, 0) < 0) + return -errno; + + return 0; + } + + if (setsockopt(fd, SOL_SOCKET, SO_BINDTOIFINDEX, &ifindex, sizeof(ifindex)) >= 0) + return 0; + if (errno != ENOPROTOOPT) + return -errno; + + /* Fall back to SO_BINDTODEVICE on kernels < 5.0 which didn't have SO_BINDTOIFINDEX */ + if (!if_indextoname(ifindex, ifname)) + return -errno; + + return socket_bind_to_ifname(fd, ifname); +} diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index 574d2b73f..6920fd99b 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -198,3 +198,6 @@ static inline int setsockopt_int(int fd, int level, int optname, int value) { return 0; } + +int socket_bind_to_ifname(int fd, const char *ifname); +int socket_bind_to_ifindex(int fd, int ifindex); diff --git a/src/basic/sort-util.c b/src/basic/sort-util.c new file mode 100644 index 000000000..5cf0d1d49 --- /dev/null +++ b/src/basic/sort-util.c @@ -0,0 +1,27 @@ +#include "sort-util.h" +#include "alloc-util.h" + +/* 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, + __compar_d_fn_t compar, void *arg) { + size_t l, u, idx; + const void *p; + int comparison; + + assert(!size_multiply_overflow(nmemb, size)); + + l = 0; + u = nmemb; + while (l < u) { + idx = (l + u) / 2; + p = (const uint8_t*) base + idx * size; + comparison = compar(key, p, arg); + if (comparison < 0) + u = idx; + else if (comparison > 0) + l = idx + 1; + else + return (void *)p; + } + return NULL; +} diff --git a/src/basic/sort-util.h b/src/basic/sort-util.h new file mode 100644 index 000000000..e029f8646 --- /dev/null +++ b/src/basic/sort-util.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "macro.h" + +void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size, + __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, __compar_fn_t compar) { + if (nmemb <= 0) + return NULL; + + assert(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, __compar_fn_t compar) { + if (nmemb <= 1) + return; + + assert(base); + qsort(base, nmemb, size, compar); +} + +/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so + * is the prototype for the comparison function */ +#define typesafe_qsort(p, n, func) \ + ({ \ + int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ + qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ + }) + +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; + + assert(base); + qsort_r(base, nmemb, size, compar, userdata); +} + +#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); \ + }) diff --git a/src/basic/special.h b/src/basic/special.h index 379a3d797..add1c1d50 100644 --- a/src/basic/special.h +++ b/src/basic/special.h @@ -76,9 +76,11 @@ /* Magic early boot services */ #define SPECIAL_FSCK_SERVICE "systemd-fsck@.service" +#define SPECIAL_FSCK_ROOT_SERVICE "systemd-fsck-root.service" #define SPECIAL_QUOTACHECK_SERVICE "systemd-quotacheck.service" #define SPECIAL_QUOTAON_SERVICE "quotaon.service" #define SPECIAL_REMOUNT_FS_SERVICE "systemd-remount-fs.service" +#define SPECIAL_VOLATILE_ROOT_SERVICE "systemd-volatile-root.service" /* Services systemd relies on */ #define SPECIAL_DBUS_SERVICE "dbus.service" diff --git a/src/basic/stat-util.c b/src/basic/stat-util.c index ea2bbc368..2cd722c10 100644 --- a/src/basic/stat-util.c +++ b/src/basic/stat-util.c @@ -223,52 +223,6 @@ int fd_is_network_fs(int fd) { return is_network_fs(&s); } -int fd_is_network_ns(int fd) { - struct statfs s; - int 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 (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; -} - int path_is_temporary_fs(const char *path) { _cleanup_close_ int fd = -1; diff --git a/src/basic/stat-util.h b/src/basic/stat-util.h index 74fb7251b..7824af350 100644 --- a/src/basic/stat-util.h +++ b/src/basic/stat-util.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include #include #include #include @@ -50,8 +51,6 @@ bool is_network_fs(const struct statfs *s) _pure_; int fd_is_temporary_fs(int fd); int fd_is_network_fs(int fd); -int fd_is_network_ns(int fd); - int path_is_temporary_fs(const char *path); /* Because statfs.t_type can be int on some architectures, we have to cast diff --git a/src/basic/stdio-util.h b/src/basic/stdio-util.h index dc67b6e76..c3b9448d4 100644 --- a/src/basic/stdio-util.h +++ b/src/basic/stdio-util.h @@ -7,7 +7,7 @@ #include #include "macro.h" -#include "util.h" +#include "memory-util.h" #define snprintf_ok(buf, len, fmt, ...) \ ((size_t) snprintf(buf, len, fmt, __VA_ARGS__) < (len)) diff --git a/src/basic/strbuf.c b/src/basic/strbuf.c index 81f4f21ad..769b22aba 100644 --- a/src/basic/strbuf.c +++ b/src/basic/strbuf.c @@ -5,8 +5,8 @@ #include #include "alloc-util.h" +#include "sort-util.h" #include "strbuf.h" -#include "util.h" /* * Strbuf stores given strings in a single continuous allocated memory diff --git a/src/basic/string-table.h b/src/basic/string-table.h index 228c12ad0..42fe4f431 100644 --- a/src/basic/string-table.h +++ b/src/basic/string-table.h @@ -59,13 +59,13 @@ 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) \ scope type name##_from_string(const char *s) { \ - type i; \ unsigned u = 0; \ + type i; \ if (!s) \ return (type) -1; \ - for (i = 0; i < (type) ELEMENTSOF(name##_table); i++) \ - if (streq_ptr(name##_table[i], s)) \ - return i; \ + i = (type) string_table_lookup(name##_table, ELEMENTSOF(name##_table), s); \ + if (i >= 0) \ + return i; \ if (safe_atou(s, &u) >= 0 && u <= max) \ return (type) u; \ return (type) -1; \ diff --git a/src/basic/string-util.c b/src/basic/string-util.c index 93917bc0f..5001a2be3 100644 --- a/src/basic/string-util.c +++ b/src/basic/string-util.c @@ -10,14 +10,15 @@ #include "alloc-util.h" #include "escape.h" +#include "fileio.h" #include "gunicode.h" #include "locale-util.h" #include "macro.h" +#include "memory-util.h" #include "string-util.h" #include "terminal-util.h" #include "utf8.h" #include "util.h" -#include "fileio.h" int strcmp_ptr(const char *a, const char *b) { @@ -675,19 +676,6 @@ char *cellescape(char *buf, size_t len, const char *s) { return buf; } -bool nulstr_contains(const char *nulstr, const char *needle) { - const char *i; - - if (!nulstr) - return false; - - NULSTR_FOREACH(i, nulstr) - if (streq(i, needle)) - return true; - - return false; -} - char* strshorten(char *s, size_t l) { assert(s); @@ -1048,25 +1036,6 @@ int free_and_strndup(char **p, const char *s, size_t l) { return 1; } -#if !HAVE_EXPLICIT_BZERO -/* - * Pointer to memset is volatile so that compiler must de-reference - * the pointer and can't assume that it points to any function in - * particular (such as memset, which it then might further "optimize") - * This approach is inspired by openssl's crypto/mem_clr.c. - */ -typedef void *(*memset_t)(void *,int,size_t); - -static volatile memset_t memset_func = memset; - -void* explicit_bzero_safe(void *p, size_t l) { - if (l > 0) - memset_func(p, '\0', l); - - return p; -} -#endif - char* string_erase(char *x) { if (!x) return NULL; diff --git a/src/basic/string-util.h b/src/basic/string-util.h index 38070abb2..b23f4c834 100644 --- a/src/basic/string-util.h +++ b/src/basic/string-util.h @@ -57,6 +57,16 @@ static inline const char *empty_to_dash(const char *str) { return isempty(str) ? "-" : str; } +static inline bool empty_or_dash(const char *str) { + return !str || + str[0] == 0 || + (str[0] == '-' && str[1] == 0); +} + +static inline const char *empty_or_dash_to_null(const char *p) { + return empty_or_dash(p) ? NULL : p; +} + static inline char *startswith(const char *s, const char *prefix) { size_t l; @@ -165,8 +175,6 @@ char *cellescape(char *buf, size_t len, const char *s); /* This limit is arbitrary, enough to give some idea what the string contains */ #define CELLESCAPE_DEFAULT_LENGTH 64 -bool nulstr_contains(const char *nulstr, const char *needle); - char* strshorten(char *s, size_t l); char *strreplace(const char *text, const char *old_string, const char *new_string); @@ -182,34 +190,13 @@ 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); +static inline int free_and_strdup_warn(char **p, const char *s) { + if (free_and_strdup(p, s) < 0) + return log_oom(); + return 0; +} 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) { - - if (needlelen <= 0) - return (void*) haystack; - - if (haystacklen < needlelen) - return NULL; - - assert(haystack); - assert(needle); - - return memmem(haystack, haystacklen, needle, needlelen); -} - -#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); char *string_free_erase(char *s); diff --git a/src/basic/strv.c b/src/basic/strv.c index 3a62f25de..21c106149 100644 --- a/src/basic/strv.c +++ b/src/basic/strv.c @@ -11,9 +11,10 @@ #include "escape.h" #include "extract-word.h" #include "fileio.h" +#include "nulstr-util.h" +#include "sort-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" char *strv_find(char **l, const char *name) { char **i; diff --git a/src/basic/strv.h b/src/basic/strv.h index 392cab65b..aa5f95ab7 100644 --- a/src/basic/strv.h +++ b/src/basic/strv.h @@ -5,12 +5,12 @@ #include #include #include +#include #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_; char *strv_find_prefix(char **l, const char *name) _pure_; diff --git a/src/basic/terminal-util.c b/src/basic/terminal-util.c index 0f3812072..b692c52e5 100644 --- a/src/basic/terminal-util.c +++ b/src/basic/terminal-util.c @@ -32,6 +32,7 @@ #include "io-util.h" #include "log.h" #include "macro.h" +#include "namespace-util.h" #include "parse-util.h" #include "path-util.h" #include "proc-cmdline.h" @@ -890,50 +891,39 @@ bool on_tty(void) { } int getttyname_malloc(int fd, char **ret) { - size_t l = 100; + char path[PATH_MAX], *c; /* PATH_MAX is counted *with* the trailing NUL byte */ int r; assert(fd >= 0); assert(ret); - for (;;) { - char path[l]; + r = ttyname_r(fd, path, sizeof path); /* positive error */ + assert(r >= 0); + if (r == ERANGE) + return -ENAMETOOLONG; + if (r > 0) + return -r; - r = ttyname_r(fd, path, sizeof(path)); - if (r == 0) { - char *c; - - c = strdup(skip_dev_prefix(path)); - if (!c) - return -ENOMEM; - - *ret = c; - return 0; - } - - if (r != ERANGE) - return -r; - - l *= 2; - } + c = strdup(skip_dev_prefix(path)); + if (!c) + return -ENOMEM; + *ret = c; return 0; } -int getttyname_harder(int fd, char **r) { - int k; - char *s = NULL; +int getttyname_harder(int fd, char **ret) { + _cleanup_free_ char *s = NULL; + int r; - k = getttyname_malloc(fd, &s); - if (k < 0) - return k; + r = getttyname_malloc(fd, &s); + if (r < 0) + return r; - if (streq(s, "tty")) { - free(s); - return get_ctty(0, NULL, r); - } + if (streq(s, "tty")) + return get_ctty(0, NULL, ret); - *r = s; + *ret = TAKE_PTR(s); return 0; } @@ -1051,6 +1041,10 @@ int ptsname_malloc(int fd, char **ret) { } free(c); + + if (l > SIZE_MAX / 2) + return -ENOMEM; + l *= 2; } } diff --git a/src/basic/time-util.c b/src/basic/time-util.c index 62cdc305f..daf952baa 100644 --- a/src/basic/time-util.c +++ b/src/basic/time-util.c @@ -1031,6 +1031,15 @@ int parse_sec_fix_0(const char *t, usec_t *ret) { return r; } +int parse_sec_def_infinity(const char *t, usec_t *ret) { + t += strspn(t, WHITESPACE); + if (isempty(t)) { + *ret = USEC_INFINITY; + return 0; + } + return parse_sec(t, ret); +} + static const char* extract_nsec_multiplier(const char *p, nsec_t *multiplier) { static const struct { const char *suffix; @@ -1205,7 +1214,7 @@ int get_timezones(char ***ret) { n_allocated = 2; n_zones = 1; - f = fopen("/usr/share/zoneinfo/zone.tab", "re"); + f = fopen("/usr/share/zoneinfo/zone1970.tab", "re"); if (f) { for (;;) { _cleanup_free_ char *line = NULL; diff --git a/src/basic/time-util.h b/src/basic/time-util.h index 531630506..a238f6914 100644 --- a/src/basic/time-util.h +++ b/src/basic/time-util.h @@ -112,6 +112,7 @@ int parse_timestamp(const char *t, usec_t *usec); int parse_sec(const char *t, usec_t *usec); int parse_sec_fix_0(const char *t, usec_t *usec); +int parse_sec_def_infinity(const char *t, usec_t *usec); int parse_time(const char *t, usec_t *usec, usec_t default_unit); int parse_nsec(const char *t, nsec_t *nsec); diff --git a/src/basic/user-util.c b/src/basic/user-util.c index 260f3d205..a479590e4 100644 --- a/src/basic/user-util.c +++ b/src/basic/user-util.c @@ -80,7 +80,7 @@ char* getlogname_malloc(void) { char *getusername_malloc(void) { const char *e; - e = getenv("USER"); + e = secure_getenv("USER"); if (e) return strdup(e); @@ -238,14 +238,21 @@ int get_user_creds( } if (home) { - if (FLAGS_SET(flags, USER_CREDS_CLEAN) && empty_or_root(p->pw_dir)) - *home = NULL; + if (FLAGS_SET(flags, USER_CREDS_CLEAN) && + (empty_or_root(p->pw_dir) || + !path_is_valid(p->pw_dir) || + !path_is_absolute(p->pw_dir))) + *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */ else *home = p->pw_dir; } if (shell) { - if (FLAGS_SET(flags, USER_CREDS_CLEAN) && (isempty(p->pw_shell) || is_nologin_shell(p->pw_shell))) + if (FLAGS_SET(flags, USER_CREDS_CLEAN) && + (isempty(p->pw_shell) || + !path_is_valid(p->pw_dir) || + !path_is_absolute(p->pw_shell) || + is_nologin_shell(p->pw_shell))) *shell = NULL; else *shell = p->pw_shell; @@ -343,6 +350,9 @@ char* uid_to_name(uid_t uid) { if (r != ERANGE) break; + if (bufsize > LONG_MAX/2) /* overflow check */ + return NULL; + bufsize *= 2; } } @@ -384,6 +394,9 @@ char* gid_to_name(gid_t gid) { if (r != ERANGE) break; + if (bufsize > LONG_MAX/2) /* overflow check */ + return NULL; + bufsize *= 2; } } @@ -445,12 +458,12 @@ int get_home_dir(char **_h) { /* Take the user specified one */ e = secure_getenv("HOME"); - if (e && path_is_absolute(e)) { + if (e && path_is_valid(e) && path_is_absolute(e)) { h = strdup(e); if (!h) return -ENOMEM; - *_h = h; + *_h = path_simplify(h, true); return 0; } @@ -480,14 +493,15 @@ int get_home_dir(char **_h) { if (!p) return errno > 0 ? -errno : -ESRCH; - if (!path_is_absolute(p->pw_dir)) + if (!path_is_valid(p->pw_dir) || + !path_is_absolute(p->pw_dir)) return -EINVAL; h = strdup(p->pw_dir); if (!h) return -ENOMEM; - *_h = h; + *_h = path_simplify(h, true); return 0; } @@ -500,13 +514,13 @@ int get_shell(char **_s) { assert(_s); /* Take the user specified one */ - e = getenv("SHELL"); - if (e) { + e = secure_getenv("SHELL"); + if (e && path_is_valid(e) && path_is_absolute(e)) { s = strdup(e); if (!s) return -ENOMEM; - *_s = s; + *_s = path_simplify(s, true); return 0; } @@ -536,14 +550,15 @@ int get_shell(char **_s) { if (!p) return errno > 0 ? -errno : -ESRCH; - if (!path_is_absolute(p->pw_shell)) + if (!path_is_valid(p->pw_shell) || + !path_is_absolute(p->pw_shell)) return -EINVAL; s = strdup(p->pw_shell); if (!s) return -ENOMEM; - *_s = s; + *_s = path_simplify(s, true); return 0; } diff --git a/src/basic/utf8.c b/src/basic/utf8.c index e0d1949dc..090c69d14 100644 --- a/src/basic/utf8.c +++ b/src/basic/utf8.c @@ -61,12 +61,7 @@ static bool unichar_is_control(char32_t ch) { } /* count of characters used to encode one unicode char */ -static size_t utf8_encoded_expected_len(const char *str) { - uint8_t c; - - assert(str); - - c = (uint8_t) str[0]; +static size_t utf8_encoded_expected_len(uint8_t c) { if (c < 0x80) return 1; if ((c & 0xe0) == 0xc0) @@ -90,7 +85,7 @@ int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar) { assert(str); - len = utf8_encoded_expected_len(str); + len = utf8_encoded_expected_len(str[0]); switch (len) { case 1: @@ -133,14 +128,14 @@ bool utf8_is_printable_newline(const char* str, size_t length, bool newline) { assert(str); - for (p = str; length;) { + for (p = str; length > 0;) { int encoded_len, r; char32_t val; - encoded_len = utf8_encoded_valid_unichar(p); - if (encoded_len < 0 || - (size_t) encoded_len > length) + encoded_len = utf8_encoded_valid_unichar(p, length); + if (encoded_len < 0) return false; + assert(encoded_len > 0 && (size_t) encoded_len <= length); r = utf8_encoded_to_unichar(p, &val); if (r < 0 || @@ -164,7 +159,7 @@ char *utf8_is_valid(const char *str) { while (*p) { int len; - len = utf8_encoded_valid_unichar(p); + len = utf8_encoded_valid_unichar(p, (size_t) -1); if (len < 0) return NULL; @@ -186,7 +181,7 @@ char *utf8_escape_invalid(const char *str) { while (*str) { int len; - len = utf8_encoded_valid_unichar(str); + len = utf8_encoded_valid_unichar(str, (size_t) -1); if (len > 0) { s = mempcpy(s, str, len); str += len; @@ -213,7 +208,7 @@ char *utf8_escape_non_printable(const char *str) { while (*str) { int len; - len = utf8_encoded_valid_unichar(str); + len = utf8_encoded_valid_unichar(str, (size_t) -1); if (len > 0) { if (utf8_is_printable(str, len)) { s = mempcpy(s, str, len); @@ -405,7 +400,7 @@ char16_t *utf8_to_utf16(const char *s, size_t length) { char32_t unichar; size_t e; - e = utf8_encoded_expected_len(s + i); + e = utf8_encoded_expected_len(s[i]); if (e <= 1) /* Invalid and single byte characters are copied as they are */ goto copy; @@ -457,17 +452,24 @@ 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 utf8_encoded_valid_unichar(const char *str, size_t length /* bytes */) { char32_t unichar; size_t len, i; int r; assert(str); + assert(length > 0); - len = utf8_encoded_expected_len(str); + /* We read until NUL, at most length bytes. (size_t) -1 may be used to disable the length check. */ + + len = utf8_encoded_expected_len(str[0]); if (len == 0) return -EINVAL; + /* Do we have a truncated multi-byte character? */ + if (len > length) + return -EINVAL; + /* ascii is valid */ if (len == 1) return 1; @@ -500,7 +502,7 @@ size_t utf8_n_codepoints(const char *str) { while (*str != 0) { int k; - k = utf8_encoded_valid_unichar(str); + k = utf8_encoded_valid_unichar(str, (size_t) -1); if (k < 0) return (size_t) -1; diff --git a/src/basic/utf8.h b/src/basic/utf8.h index 628456936..6df70921d 100644 --- a/src/basic/utf8.h +++ b/src/basic/utf8.h @@ -32,7 +32,7 @@ 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_valid_unichar(const char *str, size_t length); int utf8_encoded_to_unichar(const char *str, char32_t *ret_unichar); static inline bool utf16_is_surrogate(char16_t c) { diff --git a/src/basic/util.c b/src/basic/util.c index e577c93e6..93d610bc9 100644 --- a/src/basic/util.c +++ b/src/basic/util.c @@ -19,7 +19,6 @@ #include "alloc-util.h" #include "btrfs-util.h" #include "build.h" -#include "cgroup-util.h" #include "def.h" #include "device-nodes.h" #include "dirent-util.h" @@ -52,33 +51,6 @@ int saved_argc = 0; char **saved_argv = NULL; static int saved_in_initrd = -1; -size_t page_size(void) { - static thread_local size_t pgsz = 0; - long r; - - if (_likely_(pgsz > 0)) - return pgsz; - - r = sysconf(_SC_PAGESIZE); - assert(r > 0); - - pgsz = (size_t) r; - return pgsz; -} - -bool plymouth_running(void) { - return access("/run/plymouth/pid", F_OK) >= 0; -} - -bool display_is_local(const char *display) { - assert(display); - - return - display[0] == ':' && - display[1] >= '0' && - display[1] <= '9'; -} - bool kexec_loaded(void) { _cleanup_free_ char *s = NULL; @@ -141,53 +113,6 @@ void in_initrd_force(bool value) { saved_in_initrd = 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, - __compar_d_fn_t compar, void *arg) { - size_t l, u, idx; - const void *p; - int comparison; - - assert(!size_multiply_overflow(nmemb, size)); - - l = 0; - u = nmemb; - while (l < u) { - idx = (l + u) / 2; - p = (const uint8_t*) base + idx * size; - comparison = compar(key, p, arg); - if (comparison < 0) - u = idx; - else if (comparison > 0) - l = idx + 1; - else - return (void *)p; - } - 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; @@ -294,268 +219,6 @@ int container_get_leader(const char *machine, pid_t *pid) { return 0; } -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd) { - _cleanup_close_ int pidnsfd = -1, mntnsfd = -1, netnsfd = -1, usernsfd = -1; - int rfd = -1; - - assert(pid >= 0); - - if (mntns_fd) { - const char *mntns; - - mntns = procfs_file_alloca(pid, "ns/mnt"); - mntnsfd = open(mntns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (mntnsfd < 0) - return -errno; - } - - if (pidns_fd) { - const char *pidns; - - pidns = procfs_file_alloca(pid, "ns/pid"); - pidnsfd = open(pidns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (pidnsfd < 0) - return -errno; - } - - if (netns_fd) { - const char *netns; - - netns = procfs_file_alloca(pid, "ns/net"); - netnsfd = open(netns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (netnsfd < 0) - return -errno; - } - - if (userns_fd) { - const char *userns; - - userns = procfs_file_alloca(pid, "ns/user"); - usernsfd = open(userns, O_RDONLY|O_NOCTTY|O_CLOEXEC); - if (usernsfd < 0 && errno != ENOENT) - return -errno; - } - - if (root_fd) { - const char *root; - - root = procfs_file_alloca(pid, "root"); - rfd = open(root, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); - if (rfd < 0) - return -errno; - } - - if (pidns_fd) - *pidns_fd = pidnsfd; - - if (mntns_fd) - *mntns_fd = mntnsfd; - - if (netns_fd) - *netns_fd = netnsfd; - - if (userns_fd) - *userns_fd = usernsfd; - - if (root_fd) - *root_fd = rfd; - - pidnsfd = mntnsfd = netnsfd = usernsfd = -1; - - return 0; -} - -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd) { - if (userns_fd >= 0) { - /* Can't setns to your own userns, since then you could - * escalate from non-root to root in your own namespace, so - * check if namespaces equal before attempting to enter. */ - _cleanup_free_ char *userns_fd_path = NULL; - int r; - if (asprintf(&userns_fd_path, "/proc/self/fd/%d", userns_fd) < 0) - return -ENOMEM; - - r = files_same(userns_fd_path, "/proc/self/ns/user", 0); - if (r < 0) - return r; - if (r) - userns_fd = -1; - } - - if (pidns_fd >= 0) - if (setns(pidns_fd, CLONE_NEWPID) < 0) - return -errno; - - if (mntns_fd >= 0) - if (setns(mntns_fd, CLONE_NEWNS) < 0) - return -errno; - - if (netns_fd >= 0) - if (setns(netns_fd, CLONE_NEWNET) < 0) - return -errno; - - if (userns_fd >= 0) - if (setns(userns_fd, CLONE_NEWUSER) < 0) - return -errno; - - if (root_fd >= 0) { - if (fchdir(root_fd) < 0) - return -errno; - - if (chroot(".") < 0) - return -errno; - } - - return reset_uid_gid(); -} - -uint64_t physical_memory(void) { - _cleanup_free_ char *root = NULL, *value = NULL; - uint64_t mem, lim; - size_t ps; - long sc; - int r; - - /* We return this as uint64_t in case we are running as 32bit process on a 64bit kernel with huge amounts of - * memory. - * - * In order to support containers nicely that have a configured memory limit we'll take the minimum of the - * physically reported amount of memory and the limit configured for the root cgroup, if there is any. */ - - sc = sysconf(_SC_PHYS_PAGES); - assert(sc > 0); - - ps = page_size(); - mem = (uint64_t) sc * (uint64_t) ps; - - r = cg_get_root_path(&root); - if (r < 0) { - log_debug_errno(r, "Failed to determine root cgroup, ignoring cgroup memory limit: %m"); - return mem; - } - - r = cg_all_unified(); - if (r < 0) { - log_debug_errno(r, "Failed to determine root unified mode, ignoring cgroup memory limit: %m"); - return mem; - } - if (r > 0) { - r = cg_get_attribute("memory", root, "memory.max", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.max cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - - if (streq(value, "max")) - return mem; - } else { - r = cg_get_attribute("memory", root, "memory.limit_in_bytes", &value); - if (r < 0) { - log_debug_errno(r, "Failed to read memory.limit_in_bytes cgroup attribute, ignoring cgroup memory limit: %m"); - return mem; - } - } - - r = safe_atou64(value, &lim); - if (r < 0) { - log_debug_errno(r, "Failed to parse cgroup memory limit '%s', ignoring: %m", value); - return mem; - } - if (lim == UINT64_MAX) - return mem; - - /* Make sure the limit is a multiple of our own page size */ - lim /= ps; - lim *= ps; - - return MIN(mem, lim); -} - -uint64_t physical_memory_scale(uint64_t v, uint64_t max) { - uint64_t p, m, ps, r; - - assert(max > 0); - - /* Returns the physical memory size, multiplied by v divided by max. Returns UINT64_MAX on overflow. On success - * the result is a multiple of the page size (rounds down). */ - - ps = page_size(); - assert(ps > 0); - - p = physical_memory() / ps; - assert(p > 0); - - m = p * v; - if (m / p != v) - return UINT64_MAX; - - m /= max; - - r = m * ps; - if (r / ps != m) - return UINT64_MAX; - - return r; -} - -uint64_t system_tasks_max(void) { - - uint64_t a = TASKS_MAX, b = TASKS_MAX; - _cleanup_free_ char *root = NULL; - int r; - - /* Determine the maximum number of tasks that may run on this system. We check three sources to determine this - * limit: - * - * a) the maximum tasks value the kernel allows on this architecture - * b) the cgroups pids_max attribute for the system - * c) the kernel's configured maximum PID value - * - * And then pick the smallest of the three */ - - r = procfs_tasks_get_limit(&a); - if (r < 0) - log_debug_errno(r, "Failed to read maximum number of tasks from /proc, ignoring: %m"); - - r = cg_get_root_path(&root); - if (r < 0) - log_debug_errno(r, "Failed to determine cgroup root path, ignoring: %m"); - else { - _cleanup_free_ char *value = NULL; - - r = cg_get_attribute("pids", root, "pids.max", &value); - if (r < 0) - log_debug_errno(r, "Failed to read pids.max attribute of cgroup root, ignoring: %m"); - else if (!streq(value, "max")) { - r = safe_atou64(value, &b); - if (r < 0) - log_debug_errno(r, "Failed to parse pids.max attribute of cgroup root, ignoring: %m"); - } - } - - return MIN3(TASKS_MAX, - a <= 0 ? TASKS_MAX : a, - b <= 0 ? TASKS_MAX : b); -} - -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) { - uint64_t t, m; - - assert(max > 0); - - /* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages - * relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */ - - t = system_tasks_max(); - assert(t > 0); - - m = t * v; - if (m / t != v) /* overflow? */ - return UINT64_MAX; - - return m / max; -} - int version(void) { puts("systemd " STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")\n" SYSTEMD_FEATURES); diff --git a/src/basic/util.h b/src/basic/util.h index dc33d6606..25e6ab811 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -1,34 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "format-util.h" #include "macro.h" -#include "time-util.h" - -size_t page_size(void) _pure_; -#define PAGE_ALIGN(l) ALIGN_TO((l), page_size()) static inline const char* yes_no(bool b) { return b ? "yes" : "no"; @@ -46,19 +21,14 @@ static inline const char* enable_disable(bool b) { return b ? "enable" : "disable"; } -bool plymouth_running(void); - -bool display_is_local(const char *display) _pure_; - -#define NULSTR_FOREACH(i, l) \ - for ((i) = (l); (i) && *(i); (i) = strchr((i), 0)+1) - -#define NULSTR_FOREACH_PAIR(i, j, l) \ - for ((i) = (l), (j) = strchr((i), 0)+1; (i) && *(i); (i) = strchr((j), 0)+1, (j) = *(i) ? strchr((i), 0)+1 : (i)) - extern int saved_argc; extern char **saved_argv; +static inline void save_argc_argv(int argc, char **argv) { + saved_argc = argc; + saved_argv = argv; +} + bool kexec_loaded(void); int prot_from_flags(int flags) _const_; @@ -66,138 +36,8 @@ int prot_from_flags(int flags) _const_; 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, - __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, __compar_fn_t compar) { - if (nmemb <= 0) - return NULL; - - assert(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, __compar_fn_t compar) { - if (nmemb <= 1) - return; - - assert(base); - qsort(base, nmemb, size, compar); -} - -/* A wrapper around the above, but that adds typesafety: the element size is automatically derived from the type and so - * is the prototype for the comparison function */ -#define typesafe_qsort(p, n, func) \ - ({ \ - int (*_func_)(const typeof(p[0])*, const typeof(p[0])*) = func; \ - qsort_safe((p), (n), sizeof((p)[0]), (__compar_fn_t) _func_); \ - }) - -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; - - assert(base); - qsort_r(base, nmemb, size, compar, userdata); -} - -#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; - assert(src); - 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) \ - ({ \ - 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; -} - -static inline void _reset_errno_(int *saved_errno) { - if (*saved_errno < 0) /* Invalidated by UNPROTECT_ERRNO? */ - return; - - errno = *saved_errno; -} - -#define PROTECT_ERRNO \ - _cleanup_(_reset_errno_) _unused_ int _saved_errno_ = errno - -#define UNPROTECT_ERRNO \ - do { \ - errno = _saved_errno_; \ - _saved_errno_ = -1; \ - } while (false) - -static inline int negative_errno(void) { - /* This helper should be used to shut up gcc if you know 'errno' is - * negative. Instead of "return -errno;", use "return negative_errno();" - * It will suppress bogus gcc warnings in case it assumes 'errno' might - * be 0 and thus the caller's error-handling might not be triggered. */ - assert_return(errno > 0, -EINVAL); - return -errno; -} - static inline unsigned u64log2(uint64_t n) { #if __SIZEOF_LONG_LONG__ == 8 return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; @@ -237,15 +77,6 @@ static inline unsigned log2u_round_up(unsigned x) { int container_get_leader(const char *machine, pid_t *pid); -int namespace_open(pid_t pid, int *pidns_fd, int *mntns_fd, int *netns_fd, int *userns_fd, int *root_fd); -int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int root_fd); - -uint64_t physical_memory(void); -uint64_t physical_memory_scale(uint64_t v, uint64_t max); - -uint64_t system_tasks_max(void); -uint64_t system_tasks_max_scale(uint64_t v, uint64_t max); - int version(void); int str_verscmp(const char *s1, const char *s2); diff --git a/src/basic/virt.c b/src/basic/virt.c index f63f15f6c..5dd1bd663 100644 --- a/src/basic/virt.c +++ b/src/basic/virt.c @@ -40,6 +40,8 @@ static int detect_vm_cpuid(void) { /* https://wiki.freebsd.org/bhyve */ { "bhyve bhyve ", VIRTUALIZATION_BHYVE }, { "QNXQVMBSQG", VIRTUALIZATION_QNX }, + /* https://projectacrn.org */ + { "ACRNACRNACRN", VIRTUALIZATION_ACRN }, }; uint32_t eax, ebx, ecx, edx; @@ -202,7 +204,7 @@ static int detect_vm_xen_dom0(void) { r = read_one_line_file(PATH_FEATURES, &domcap); if (r < 0 && r != -ENOENT) return r; - if (r == 0) { + if (r >= 0) { unsigned long features; /* Here, we need to use sscanf() instead of safe_atoul() @@ -436,10 +438,12 @@ int detect_container(void) { { "systemd-nspawn", VIRTUALIZATION_SYSTEMD_NSPAWN }, { "docker", VIRTUALIZATION_DOCKER }, { "rkt", VIRTUALIZATION_RKT }, + { "wsl", VIRTUALIZATION_WSL }, }; static thread_local int cached_found = _VIRTUALIZATION_INVALID; _cleanup_free_ char *m = NULL; + _cleanup_free_ char *o = NULL; const char *e = NULL; unsigned j; int r; @@ -454,6 +458,15 @@ int detect_container(void) { goto finish; } + /* "Official" way of detecting WSL https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364 */ + r = read_one_line_file("/proc/sys/kernel/osrelease", &o); + if (r >= 0) { + if (strstr(o, "Microsoft") || strstr(o, "WSL")) { + r = VIRTUALIZATION_WSL; + goto finish; + } + } + if (getpid_cached() == 1) { /* If we are PID 1 we can just check our own environment variable, and that's authoritative. */ @@ -469,11 +482,11 @@ int detect_container(void) { /* Otherwise, PID 1 might have dropped this information into a file in /run. This is better than accessing * /proc/1/environ, since we don't need CAP_SYS_PTRACE for that. */ r = read_one_line_file("/run/systemd/container", &m); - if (r >= 0) { + if (r > 0) { e = m; goto translate_name; } - if (r != -ENOENT) + if (!IN_SET(r, -ENOENT, 0)) return log_debug_errno(r, "Failed to read /run/systemd/container: %m"); /* Fallback for cases where PID 1 was not systemd (for example, cases where init=/bin/sh is used. */ @@ -628,6 +641,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_PARALLELS] = "parallels", [VIRTUALIZATION_BHYVE] = "bhyve", [VIRTUALIZATION_QNX] = "qnx", + [VIRTUALIZATION_ACRN] = "acrn", [VIRTUALIZATION_VM_OTHER] = "vm-other", [VIRTUALIZATION_SYSTEMD_NSPAWN] = "systemd-nspawn", @@ -636,6 +650,7 @@ static const char *const virtualization_table[_VIRTUALIZATION_MAX] = { [VIRTUALIZATION_OPENVZ] = "openvz", [VIRTUALIZATION_DOCKER] = "docker", [VIRTUALIZATION_RKT] = "rkt", + [VIRTUALIZATION_WSL] = "wsl", [VIRTUALIZATION_CONTAINER_OTHER] = "container-other", }; diff --git a/src/basic/virt.h b/src/basic/virt.h index c4cf4bfea..c0836897f 100644 --- a/src/basic/virt.h +++ b/src/basic/virt.h @@ -21,6 +21,7 @@ enum { VIRTUALIZATION_PARALLELS, VIRTUALIZATION_BHYVE, VIRTUALIZATION_QNX, + VIRTUALIZATION_ACRN, VIRTUALIZATION_VM_OTHER, VIRTUALIZATION_VM_LAST = VIRTUALIZATION_VM_OTHER, @@ -31,6 +32,7 @@ enum { VIRTUALIZATION_OPENVZ, VIRTUALIZATION_DOCKER, VIRTUALIZATION_RKT, + VIRTUALIZATION_WSL, VIRTUALIZATION_CONTAINER_OTHER, VIRTUALIZATION_CONTAINER_LAST = VIRTUALIZATION_CONTAINER_OTHER, diff --git a/src/basic/xattr-util.c b/src/basic/xattr-util.c index 0ee097983..1d045f911 100644 --- a/src/basic/xattr-util.c +++ b/src/basic/xattr-util.c @@ -140,7 +140,12 @@ static int parse_crtime(le64_t le, usec_t *usec) { } int fd_getcrtime_at(int dirfd, const char *name, usec_t *ret, int flags) { - struct_statx sx; + struct_statx sx +#if HAS_FEATURE_MEMORY_SANITIZER + = {} +# warning "Explicitly initializing struct statx, to work around msan limitation. Please remove as soon as msan has been updated to not require this." +#endif + ; usec_t a, b; le64_t le; size_t n; diff --git a/src/binfmt/binfmt.c b/src/binfmt/binfmt.c index af31f0967..66e2f0131 100644 --- a/src/binfmt/binfmt.c +++ b/src/binfmt/binfmt.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "alloc-util.h" #include "conf-files.h" @@ -20,7 +22,6 @@ #include "pretty-print.h" #include "string-util.h" #include "strv.h" -#include "util.h" static bool arg_cat_config = false; static PagerFlags arg_pager_flags = 0; diff --git a/src/boot/bless-boot.c b/src/boot/bless-boot.c index 42b9618aa..b5d110f42 100644 --- a/src/boot/bless-boot.c +++ b/src/boot/bless-boot.c @@ -16,9 +16,9 @@ #include "verbs.h" #include "virt.h" -static char *arg_path = NULL; +static char **arg_path = NULL; -STATIC_DESTRUCTOR_REGISTER(arg_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_path, strv_freep); static int help(int argc, char *argv[], void *userdata) { @@ -27,7 +27,7 @@ static int help(int argc, char *argv[], void *userdata) { "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" + " --path=PATH Path to the $BOOT partition (may be used multiple times)\n" "\n" "Commands:\n" " good Mark this boot as good\n" @@ -67,7 +67,7 @@ static int parse_argv(int argc, char *argv[]) { return version(); case ARG_PATH: - r = free_and_strdup(&arg_path, optarg); + r = strv_extend(&arg_path, optarg); if (r < 0) return log_oom(); break; @@ -82,20 +82,42 @@ static int parse_argv(int argc, char *argv[]) { return 1; } -static int acquire_esp(void) { - _cleanup_free_ char *np = NULL; +static int acquire_path(void) { + _cleanup_free_ char *esp_path = NULL, *xbootldr_path = NULL; + char **a; 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) + if (!strv_isempty(arg_path)) + return 0; + + r = find_esp_and_warn(NULL, false, &esp_path, NULL, NULL, NULL, NULL); + if (r < 0 && r != -ENOKEY) /* ENOKEY means not found, and is the only error the function won't log about on its own */ return r; - free_and_replace(arg_path, np); - log_debug("Using EFI System Partition at %s.", arg_path); + r = find_xbootldr_and_warn(NULL, false, &xbootldr_path, NULL); + if (r < 0 && r != -ENOKEY) + return r; + + if (!esp_path && !xbootldr_path) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "Couldn't find $BOOT partition. It is recommended to mount it to /boot.\n" + "Alternatively, use --path= to specify path to mount point."); + + if (esp_path) + a = strv_new(esp_path, xbootldr_path); + else + a = strv_new(xbootldr_path); + if (!a) + return log_oom(); + + strv_free_and_replace(arg_path, a); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *j; + + j = strv_join(arg_path, ":"); + log_debug("Using %s as boot loader drop-in search path.", j); + } return 0; } @@ -282,10 +304,9 @@ static const char *skip_slash(const char *path) { } 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; + char **p; int r; r = acquire_boot_count_path(&path, &prefix, &left, &done, &suffix); @@ -296,7 +317,7 @@ static int verb_status(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = acquire_esp(); + r = acquire_path(); if (r < 0) return r; @@ -308,50 +329,61 @@ static int verb_status(int argc, char *argv[], void *userdata) { 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("Booted file: %s\n" + "The same modified for 'good': %s\n" + "The same modified for 'bad': %s\n", + path, + good, + 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); + STRV_FOREACH(p, arg_path) { + _cleanup_close_ int fd = -1; - if (faccessat(fd, skip_slash(path), F_OK, 0) >= 0) { - puts("indeterminate"); - return 0; + fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY); + if (fd < 0) { + if (errno == ENOENT) + continue; + + return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p); + } + + 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); + + /* We didn't find any of the three? If so, let's try the next directory, before we give up. */ } - 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"); + return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "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; + char **p; int r; r = acquire_boot_count_path(&path, &prefix, NULL, &done, &suffix); @@ -360,7 +392,7 @@ static int verb_set(int argc, char *argv[], void *userdata) { if (r < 0) return r; - r = acquire_esp(); + r = acquire_path(); if (r < 0) return r; @@ -372,10 +404,6 @@ static int verb_set(int argc, char *argv[], void *userdata) { 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; @@ -392,45 +420,58 @@ static int verb_set(int argc, char *argv[], void *userdata) { source2 = bad; } - r = rename_noreplace(fd, skip_slash(source1), fd, skip_slash(target)); - if (r == -EEXIST) - goto exists; - else if (r == -ENOENT) { + STRV_FOREACH(p, arg_path) { + _cleanup_close_ int fd = -1; - r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target)); + fd = open(*p, O_DIRECTORY|O_CLOEXEC|O_RDONLY); + if (fd < 0) + return log_error_errno(errno, "Failed to open $BOOT partition '%s': %m", *p); + + r = rename_noreplace(fd, skip_slash(source1), 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? */ + r = rename_noreplace(fd, skip_slash(source2), fd, skip_slash(target)); + if (r == -EEXIST) goto exists; + else if (r == -ENOENT) { + + if (faccessat(fd, skip_slash(target), F_OK, 0) >= 0) /* Hmm, if we can't find either source file, maybe the destination already exists? */ + goto exists; + + if (errno != ENOENT) + return log_error_errno(errno, "Failed to determine if %s already exists: %m", target); + + /* We found none of the snippets here, try the next directory */ + continue; + } 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); - 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); + return log_error_errno(r, "Failed to rename '%s' to '%s': %m", source1, target); else - log_debug("Successfully renamed '%s' to '%s'.", source2, target); + log_debug("Successfully renamed '%s' to '%s'.", source1, 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(target); + if (!parent) + return log_oom(); - /* 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"); - 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 $BOOT partition, 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); + log_info("Marked boot as '%s'. (Boot attempt counter is at %" PRIu64".)", argv[0], done); + } + log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Can't find boot counter source file for '%s': %m", target); return 1; exists: diff --git a/src/boot/bootctl.c b/src/boot/bootctl.c index dc2fd9662..a7de3800f 100644 --- a/src/boot/bootctl.c +++ b/src/boot/bootctl.c @@ -35,6 +35,7 @@ #include "pretty-print.h" #include "rm-rf.h" #include "stat-util.h" +#include "stdio-util.h" #include "string-util.h" #include "strv.h" #include "terminal-util.h" @@ -45,12 +46,20 @@ #include "verbs.h" #include "virt.h" -static char *arg_path = NULL; -static bool arg_print_path = false; +static char *arg_esp_path = NULL; +static char *arg_xbootldr_path = NULL; +static bool arg_print_esp_path = false; +static bool arg_print_dollar_boot_path = false; static bool arg_touch_variables = true; static PagerFlags arg_pager_flags = 0; -STATIC_DESTRUCTOR_REGISTER(arg_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_esp_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_xbootldr_path, freep); + +static const char *arg_dollar_boot_path(void) { + /* $BOOT shall be the XBOOTLDR partition if it exists, and otherwise the ESP */ + return arg_xbootldr_path ?: arg_esp_path; +} static int acquire_esp( bool unprivileged_mode, @@ -62,24 +71,44 @@ static int acquire_esp( char *np; int r; - /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on its own, - * except for ENOKEY (which is good, we want to show our own message in that case, suggesting use of --path=) - * and EACCESS (only when we request unprivileged mode; in this case we simply eat up the error here, so that - * --list and --status work too, without noise about this). */ + /* Find the ESP, and log about errors. Note that find_esp_and_warn() will log in all error cases on + * its own, except for ENOKEY (which is good, we want to show our own message in that case, + * suggesting use of --esp-path=) and EACCESS (only when we request unprivileged mode; in this case + * we simply eat up the error here, so that --list and --status work too, without noise about + * this). */ - r = find_esp_and_warn(arg_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid); + r = find_esp_and_warn(arg_esp_path, unprivileged_mode, &np, ret_part, ret_pstart, ret_psize, ret_uuid); if (r == -ENOKEY) 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."); + "Alternatively, use --esp-path= to specify path to mount point."); if (r < 0) return r; - free_and_replace(arg_path, np); + free_and_replace(arg_esp_path, np); + log_debug("Using EFI System Partition at %s.", arg_esp_path); - log_debug("Using EFI System Partition at %s.", arg_path); + return 1; +} - return 0; +static int acquire_xbootldr(bool unprivileged_mode, sd_id128_t *ret_uuid) { + char *np; + int r; + + r = find_xbootldr_and_warn(arg_xbootldr_path, unprivileged_mode, &np, ret_uuid); + if (r == -ENOKEY) { + log_debug_errno(r, "Didn't find an XBOOTLDR partition, using the ESP as $BOOT."); + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + return 0; + } + if (r < 0) + return r; + + free_and_replace(arg_xbootldr_path, np); + log_debug("Using XBOOTLDR partition at %s as $BOOT.", arg_xbootldr_path); + + return 1; } /* search for "#### LoaderInfo: systemd-boot 218 ####" string inside the binary */ @@ -96,6 +125,10 @@ static int get_file_version(int fd, char **v) { if (fstat(fd, &st) < 0) return log_error_errno(errno, "Failed to stat EFI binary: %m"); + r = stat_verify_regular(&st); + if (r < 0) + return log_error_errno(errno, "EFI binary is not a regular file: %m"); + if (st.st_size < 27) { *v = NULL; return 0; @@ -112,8 +145,7 @@ static int get_file_version(int fd, char **v) { e = memmem(s, st.st_size - (s - buf), " ####", 5); if (!e || e - s < 3) { - log_error("Malformed version string."); - r = -EINVAL; + r = log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Malformed version string."); goto finish; } @@ -131,10 +163,13 @@ finish: } static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) { - char *p; _cleanup_closedir_ DIR *d = NULL; struct dirent *de; - int r = 0, c = 0; + int c = 0, r; + char *p; + + assert(esp_path); + assert(path); p = strjoina(esp_path, "/", path); d = opendir(p); @@ -146,8 +181,8 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char } FOREACH_DIRENT(de, d, break) { - _cleanup_close_ int fd = -1; _cleanup_free_ char *v = NULL; + _cleanup_close_ int fd = -1; if (!endswith_no_case(de->d_name, ".efi")) continue; @@ -166,6 +201,7 @@ static int enumerate_binaries(const char *esp_path, const char *path, const char 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(SPECIAL_GLYPH_TREE_RIGHT), path, de->d_name); + c++; } @@ -188,20 +224,22 @@ static int status_binaries(const char *esp_path, sd_id128_t partition) { printf("\n"); r = enumerate_binaries(esp_path, "EFI/systemd", NULL); + if (r < 0) + goto finish; if (r == 0) 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) + goto finish; if (r == 0) log_info("No default/fallback boot loader installed in ESP."); - else if (r < 0) - return r; + r = 0; + +finish: printf("\n"); - - return 0; + return r; } static int print_efi_option(uint16_t id, bool in_order) { @@ -274,13 +312,15 @@ static int status_variables(void) { static int boot_entry_show(const BootEntry *e, bool show_as_default) { assert(e); - printf(" title: %s%s%s%s%s%s\n", + printf(" title: %s%s%s%s%s%s\n" + " type: %s\n", ansi_highlight(), boot_entry_title(e), ansi_normal(), ansi_highlight_green(), show_as_default ? " (default)" : "", - ansi_normal()); + ansi_normal(), + boot_entry_type_to_string(e->type)); if (e->id) printf(" id: %s\n", e->id); @@ -316,11 +356,34 @@ static int boot_entry_show(const BootEntry *e, bool show_as_default) { return 0; } -static int status_entries(const char *esp_path, sd_id128_t partition) { +static int status_entries( + const char *esp_path, + sd_id128_t esp_partition_uuid, + const char *xbootldr_path, + sd_id128_t xbootldr_partition_uuid) { + _cleanup_(boot_config_free) BootConfig config = {}; + sd_id128_t dollar_boot_partition_uuid; + const char *dollar_boot_path; int r; - r = boot_entries_load_config(esp_path, &config); + assert(esp_path || xbootldr_path); + + if (xbootldr_path) { + dollar_boot_path = xbootldr_path; + dollar_boot_partition_uuid = xbootldr_partition_uuid; + } else { + dollar_boot_path = esp_path; + dollar_boot_partition_uuid = esp_partition_uuid; + } + + printf("Boot Loader Entries:\n" + " $BOOT: %s", dollar_boot_path); + if (!sd_id128_is_null(dollar_boot_partition_uuid)) + printf(" (/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x)", SD_ID128_FORMAT_VAL(dollar_boot_partition_uuid)); + printf("\n\n"); + + r = boot_entries_load_config(esp_path, xbootldr_path, &config); if (r < 0) return r; @@ -386,10 +449,8 @@ static int version_check(int fd_from, const char *from, int fd_to, const char *t "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); - return -ESTALE; - } + if (compare_version(a, b) < 0) + return log_warning_errno(SYNTHETIC_ERRNO(ESTALE), "Skipping \"%s\", since a newer boot loader version exists already.", to); return 0; } @@ -436,7 +497,7 @@ static int copy_file_with_version_check(const char *from, const char *to, bool f return log_error_errno(r, "Failed to copy data from \"%s\" to \"%s\": %m", from, t); } - (void) copy_times(fd_from, fd_to); + (void) copy_times(fd_from, fd_to, 0); if (fsync(fd_to) < 0) { (void) unlink_noerrno(t); @@ -456,9 +517,9 @@ static int copy_file_with_version_check(const char *from, const char *to, bool f } static int mkdir_one(const char *prefix, const char *suffix) { - char *p; + _cleanup_free_ char *p = NULL; - p = strjoina(prefix, "/", suffix); + p = path_join(prefix, suffix); if (mkdir(p, 0700) < 0) { if (errno != EEXIST) return log_error_errno(errno, "Failed to create \"%s\": %m", p); @@ -468,20 +529,21 @@ static int mkdir_one(const char *prefix, const char *suffix) { return 0; } -static const char *efi_subdirs[] = { +static const char *const esp_subdirs[] = { "EFI", "EFI/systemd", "EFI/BOOT", "loader", - "loader/entries", + /* Note that "/loader/entries" is not listed here, since it should be placed in $BOOT, which might + * not necessarily be the ESP */ NULL }; -static int create_dirs(const char *esp_path) { - const char **i; +static int create_esp_subdirs(const char *esp_path) { + const char *const *i; int r; - STRV_FOREACH(i, efi_subdirs) { + STRV_FOREACH(i, esp_subdirs) { r = mkdir_one(esp_path, *i); if (r < 0) return r; @@ -520,22 +582,11 @@ static int install_binaries(const char *esp_path, bool force) { _cleanup_closedir_ DIR *d = NULL; int r = 0; - if (force) { - /* Don't create any of these directories when we are - * just updating. When we update we'll drop-in our - * files (unless there are newer ones already), but we - * won't create the directories for them in the first - * place. */ - r = create_dirs(esp_path); - if (r < 0) - return r; - } - d = opendir(BOOTLIBDIR); if (!d) return log_error_errno(errno, "Failed to open \""BOOTLIBDIR"\": %m"); - FOREACH_DIRENT(de, d, break) { + FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read \""BOOTLIBDIR"\": %m")) { int k; if (!endswith_no_case(de->d_name, ".efi")) @@ -624,7 +675,7 @@ static int insert_into_order(uint16_t slot, bool first) { } /* extend array */ - t = realloc(order, (n + 1) * sizeof(uint16_t)); + t = reallocarray(order, n + 1, sizeof(uint16_t)); if (!t) return -ENOMEM; order = t; @@ -751,18 +802,36 @@ static int rmdir_one(const char *prefix, const char *suffix) { p = strjoina(prefix, "/", suffix); if (rmdir(p) < 0) { - if (!IN_SET(errno, ENOENT, ENOTEMPTY)) - return log_error_errno(errno, "Failed to remove \"%s\": %m", p); + bool ignore = IN_SET(errno, ENOENT, ENOTEMPTY); + + log_full_errno(ignore ? LOG_DEBUG : LOG_ERR, errno, + "Failed to remove directory \"%s\": %m", p); + if (!ignore) + return -errno; } else log_info("Removed \"%s\".", p); return 0; } +static int remove_esp_subdirs(const char *esp_path) { + size_t i; + int r = 0; + + for (i = ELEMENTSOF(esp_subdirs)-1; i > 0; i--) { + int q; + + q = rmdir_one(esp_path, esp_subdirs[i-1]); + if (q < 0 && r >= 0) + r = q; + } + + return r; +} + static int remove_binaries(const char *esp_path) { char *p; int r, q; - unsigned i; p = strjoina(esp_path, "/EFI/systemd"); r = rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL); @@ -771,15 +840,31 @@ static int remove_binaries(const char *esp_path) { if (q < 0 && r == 0) r = q; - for (i = ELEMENTSOF(efi_subdirs)-1; i > 0; i--) { - q = rmdir_one(esp_path, efi_subdirs[i-1]); - if (q < 0 && r == 0) - r = q; - } - return r; } +static int remove_loader_config(const char *esp_path) { + const char *p; + + assert(esp_path); + + p = strjoina(esp_path, "/loader/loader.conf"); + if (unlink(p) < 0) { + log_full_errno(errno == ENOENT ? LOG_DEBUG : LOG_ERR, errno, "Failed to unlink file \"%s\": %m", p); + if (errno != ENOENT) + return -errno; + } else + log_info("Removed \"%s\".", p); + + return 0; +} + +static int remove_entries_directory(const char *dollar_boot_path) { + assert(dollar_boot_path); + + return rmdir_one(dollar_boot_path, "/loader/entries"); +} + static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { uint16_t slot; int r; @@ -801,21 +886,14 @@ static int remove_variables(sd_id128_t uuid, const char *path, bool in_order) { return 0; } -static int install_loader_config(const char *esp_path) { - +static int install_loader_config(const char *esp_path, sd_id128_t machine_id) { char machine_string[SD_ID128_STRING_MAX]; _cleanup_(unlink_and_freep) char *t = NULL; _cleanup_fclose_ FILE *f = NULL; - sd_id128_t machine_id; const char *p; int r, fd; - r = sd_id128_get_machine(&machine_id); - if (r < 0) - return log_error_errno(r, "Failed to get machine id: %m"); - p = strjoina(esp_path, "/loader/loader.conf"); - if (access(p, F_OK) >= 0) /* Silently skip creation if the file already exists (early check) */ return 0; @@ -844,10 +922,26 @@ static int install_loader_config(const char *esp_path) { return log_error_errno(r, "Failed to move \"%s\" into place: %m", p); t = mfree(t); - return 1; } +static int install_entries_directories(const char *dollar_boot_path, sd_id128_t machine_id) { + int r; + char buf[SD_ID128_STRING_MAX]; + + assert(dollar_boot_path); + + /* Both /loader/entries and the entry directories themselves should be located on the same + * partition. Also create the parent directory for entry directories, so that kernel-install + * knows where to put them. */ + + r = mkdir_one(dollar_boot_path, "loader/entries"); + if (r < 0) + return r; + + return mkdir_one(dollar_boot_path, sd_id128_to_string(machine_id, buf)); +} + static int help(int argc, char *argv[], void *userdata) { _cleanup_free_ char *link = NULL; int r; @@ -858,21 +952,23 @@ static int help(int argc, char *argv[], void *userdata) { 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" - " --no-pager Do not pipe output into a pager\n" + " -h --help Show this help\n" + " --version Print version\n" + " --esp-path=PATH Path to the EFI System Partition (ESP)\n" + " --boot-path=PATH Path to the $BOOT partition\n" + " -p --print-esp-path Print path to the EFI System Partition\n" + " --print-boot-path Print path to the $BOOT partition\n" + " --no-variables Don't touch EFI variables\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" - " 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" + " status Show status of installed systemd-boot and EFI variables\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" "\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" + " 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); @@ -882,19 +978,25 @@ static int help(int argc, char *argv[], void *userdata) { static int parse_argv(int argc, char *argv[]) { enum { - ARG_PATH = 0x100, + ARG_ESP_PATH = 0x100, + ARG_BOOT_PATH, + ARG_PRINT_BOOT_PATH, ARG_VERSION, ARG_NO_VARIABLES, ARG_NO_PAGER, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "path", required_argument, NULL, ARG_PATH }, - { "print-path", no_argument, NULL, 'p' }, - { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, - { "no-pager", no_argument, NULL, ARG_NO_PAGER }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "esp-path", required_argument, NULL, ARG_ESP_PATH }, + { "path", required_argument, NULL, ARG_ESP_PATH }, /* Compatibility alias */ + { "boot-path", required_argument, NULL, ARG_BOOT_PATH }, + { "print-esp-path", no_argument, NULL, 'p' }, + { "print-path", no_argument, NULL, 'p' }, /* Compatibility alias */ + { "print-boot-path", no_argument, NULL, ARG_PRINT_BOOT_PATH }, + { "no-variables", no_argument, NULL, ARG_NO_VARIABLES }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, {} }; @@ -913,14 +1015,24 @@ static int parse_argv(int argc, char *argv[]) { case ARG_VERSION: return version(); - case ARG_PATH: - r = free_and_strdup(&arg_path, optarg); + case ARG_ESP_PATH: + r = free_and_strdup(&arg_esp_path, optarg); + if (r < 0) + return log_oom(); + break; + + case ARG_BOOT_PATH: + r = free_and_strdup(&arg_xbootldr_path, optarg); if (r < 0) return log_oom(); break; case 'p': - arg_print_path = true; + arg_print_esp_path = true; + break; + + case ARG_PRINT_BOOT_PATH: + arg_print_dollar_boot_path = true; break; case ARG_NO_VARIABLES: @@ -950,23 +1062,33 @@ static void read_loader_efi_var(const char *name, char **var) { } static int verb_status(int argc, char *argv[], void *userdata) { - - sd_id128_t uuid = SD_ID128_NULL; + sd_id128_t esp_uuid = SD_ID128_NULL, xbootldr_uuid = SD_ID128_NULL; int r, k; - r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid); - - if (arg_print_path) { + r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &esp_uuid); + if (arg_print_esp_path) { if (r == -EACCES) /* If we couldn't acquire the ESP path, log about access errors (which is the only * error the find_esp_and_warn() won't log on its own) */ - return log_error_errno(r, "Failed to determine ESP: %m"); + return log_error_errno(r, "Failed to determine ESP location: %m"); if (r < 0) return r; - puts(arg_path); - return 0; + puts(arg_esp_path); } + r = acquire_xbootldr(geteuid() != 0, &xbootldr_uuid); + if (arg_print_dollar_boot_path) { + if (r == -EACCES) + return log_error_errno(r, "Failed to determine XBOOTLDR location: %m"); + if (r < 0) + return r; + + puts(arg_dollar_boot_path()); + } + + if (arg_print_esp_path || arg_print_dollar_boot_path) + return 0; + 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 */ @@ -1037,8 +1159,8 @@ static int verb_status(int argc, char *argv[], void *userdata) { } else printf("System:\n Not booted with EFI\n\n"); - if (arg_path) { - k = status_binaries(arg_path, uuid); + if (arg_esp_path) { + k = status_binaries(arg_esp_path, esp_uuid); if (k < 0) r = k; } @@ -1049,8 +1171,8 @@ static int verb_status(int argc, char *argv[], void *userdata) { r = k; } - if (arg_path) { - k = status_entries(arg_path, uuid); + if (arg_esp_path || arg_xbootldr_path) { + k = status_entries(arg_esp_path, esp_uuid, arg_xbootldr_path, xbootldr_uuid); if (k < 0) r = k; } @@ -1060,27 +1182,29 @@ 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; int r; /* If we lack privileges we invoke find_esp_and_warn() in "unprivileged mode" here, which does two things: turn * off logging about access errors and turn off potentially privileged device probing. Here we're interested in * the latter but not the former, hence request the mode, and log about EACCES. */ - r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, &uuid); + r = acquire_esp(geteuid() != 0, NULL, NULL, NULL, NULL); if (r == -EACCES) /* We really need the ESP path for this call, hence also log about access errors */ return log_error_errno(r, "Failed to determine ESP: %m"); if (r < 0) return r; - r = boot_entries_load_config(arg_path, &config); + r = acquire_xbootldr(geteuid() != 0, NULL); + if (r == -EACCES) + return log_error_errno(r, "Failed to determine XBOOTLDR partition: %m"); if (r < 0) return r; - 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"); + r = boot_entries_load_config(arg_esp_path, arg_xbootldr_path, &config); + if (r < 0) + return r; + + (void) boot_entries_augment_from_loader(&config, false); if (config.n_entries == 0) log_info("No boot loader entries found."); @@ -1096,45 +1220,37 @@ static int verb_list(int argc, char *argv[], void *userdata) { if (r < 0) return r; - puts(""); - - strv_remove(found_by_loader, config.entries[n].id); + if (n+1 < config.n_entries) + putchar('\n'); } } - if (!strv_isempty(found_by_loader)) { - char **i; - - 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; +static int sync_everything(void) { + int ret = 0, k; - if (!arg_path) - return 0; + if (arg_esp_path) { + k = syncfs_path(AT_FDCWD, arg_esp_path); + if (k < 0) + ret = log_error_errno(k, "Failed to synchronize the ESP '%s': %m", arg_esp_path); + } - 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 (arg_xbootldr_path) { + k = syncfs_path(AT_FDCWD, arg_xbootldr_path); + if (k < 0) + ret = log_error_errno(k, "Failed to synchronize $BOOT '%s': %m", arg_xbootldr_path); + } - if (syncfs(fd) < 0) - return log_error_errno(errno, "Failed to synchronize the ESP '%s': %m", arg_path); - - return 1; + return ret; } static int verb_install(int argc, char *argv[], void *userdata) { - sd_id128_t uuid = SD_ID128_NULL; uint64_t pstart = 0, psize = 0; uint32_t part = 0; + sd_id128_t machine_id; bool install; int r; @@ -1142,24 +1258,45 @@ static int verb_install(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = acquire_xbootldr(false, NULL); + if (r < 0) + return r; + + r = sd_id128_get_machine(&machine_id); + if (r < 0) + return log_error_errno(r, "Failed to get machine id: %m"); + install = streq(argv[0], "install"); RUN_WITH_UMASK(0002) { - r = install_binaries(arg_path, install); + if (install) { + /* Don't create any of these directories when we are just updating. When we update + * we'll drop-in our files (unless there are newer ones already), but we won't create + * the directories for them in the first place. */ + r = create_esp_subdirs(arg_esp_path); + if (r < 0) + return r; + } + + r = install_binaries(arg_esp_path, install); if (r < 0) return r; if (install) { - r = install_loader_config(arg_path); + r = install_loader_config(arg_esp_path, machine_id); + if (r < 0) + return r; + + r = install_entries_directories(arg_dollar_boot_path(), machine_id); if (r < 0) return r; } } - (void) sync_esp(); + (void) sync_everything(); if (arg_touch_variables) - r = install_variables(arg_path, + r = install_variables(arg_esp_path, part, pstart, psize, uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", install); @@ -1169,21 +1306,35 @@ static int verb_install(int argc, char *argv[], void *userdata) { static int verb_remove(int argc, char *argv[], void *userdata) { sd_id128_t uuid = SD_ID128_NULL; - int r; + int r, q; r = acquire_esp(false, NULL, NULL, NULL, &uuid); if (r < 0) return r; - r = remove_binaries(arg_path); + r = acquire_xbootldr(false, NULL); + if (r < 0) + return r; - (void) sync_esp(); + r = remove_binaries(arg_esp_path); + + q = remove_loader_config(arg_esp_path); + if (q < 0 && r >= 0) + r = q; + + q = remove_entries_directory(arg_dollar_boot_path()); + if (q < 0 && r >= 0) + r = q; + + q = remove_esp_subdirs(arg_esp_path); + if (q < 0 && r >= 0) + r = q; + + (void) sync_everything(); if (arg_touch_variables) { - int q; - q = remove_variables(uuid, "/EFI/systemd/systemd-boot" EFI_MACHINE_TYPE_NAME ".efi", true); - if (q < 0 && r == 0) + if (q < 0 && r >= 0) r = q; } @@ -1261,7 +1412,7 @@ static int run(int argc, char *argv[]) { log_parse_environment(); log_open(); - /* If we run in a container, automatically turn of EFI file system access */ + /* If we run in a container, automatically turn off EFI file system access */ if (detect_container() > 0) arg_touch_variables = false; diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 9bf689583..57c423bfb 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -1,9 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include "console.h" +#include "crc32.h" #include "disk.h" #include "graphics.h" #include "linux.h" @@ -267,10 +269,8 @@ static BOOLEAN line_edit( case KEYPRESS(0, 0, CHAR_LINEFEED): case KEYPRESS(0, 0, CHAR_CARRIAGE_RETURN): - if (StrCmp(line, line_in) != 0) { - *line_out = line; - line = NULL; - } + if (StrCmp(line, line_in) != 0) + *line_out = TAKE_PTR(line); enter = TRUE; exit = TRUE; break; @@ -1256,8 +1256,7 @@ static VOID config_entry_bump_counters( /* 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; + entry->loader = TAKE_PTR(new_path); } } @@ -1316,7 +1315,7 @@ static VOID config_entry_add_from_file( entry->loader = stra_to_path(value); /* do not add an entry for ourselves */ - if (StriCmp(entry->loader, loaded_image_path) == 0) { + if (loaded_image_path && StriCmp(entry->loader, loaded_image_path) == 0) { entry->type = LOADER_UNDEFINED; break; } @@ -1358,10 +1357,8 @@ static VOID config_entry_add_from_file( s = PoolPrint(L"%s %s", entry->options, new); FreePool(entry->options); entry->options = s; - } else { - entry->options = new; - new = NULL; - } + } else + entry->options = TAKE_PTR(new); continue; } @@ -1380,10 +1377,8 @@ static VOID config_entry_add_from_file( s = PoolPrint(L"%s %s", initrd, entry->options); FreePool(entry->options); entry->options = s; - } else { - entry->options = initrd; - initrd = NULL; - } + } else + entry->options = TAKE_PTR(initrd); } entry->device = device; @@ -1836,7 +1831,7 @@ static VOID config_entry_add_osx(Config *config) { static VOID config_entry_add_linux( Config *config, - EFI_LOADED_IMAGE *loaded_image, + EFI_HANDLE *device, EFI_FILE *root_dir) { EFI_FILE_HANDLE linux_dir; @@ -1926,7 +1921,7 @@ static VOID config_entry_add_linux( 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); + entry = config_entry_add_loader(config, device, LOADER_LINUX, conf, 'l', os_name, path); FreePool(content); content = NULL; @@ -1955,6 +1950,213 @@ static VOID config_entry_add_linux( uefi_call_wrapper(linux_dir->Close, 1, linux_dir); } +/* Note that this is in GUID format, i.e. the first 32bit, and the following pair of 16bit are byteswapped. */ +static const UINT8 xbootldr_guid[16] = { + 0xff, 0xc2, 0x13, 0xbc, 0xe6, 0x59, 0x62, 0x42, 0xa3, 0x52, 0xb2, 0x75, 0xfd, 0x6f, 0x71, 0x72 +}; + +EFI_DEVICE_PATH *path_parent(EFI_DEVICE_PATH *path, EFI_DEVICE_PATH *node) { + EFI_DEVICE_PATH *parent; + UINTN len; + + len = (UINT8*) NextDevicePathNode(node) - (UINT8*) path; + parent = (EFI_DEVICE_PATH*) AllocatePool(len + sizeof(EFI_DEVICE_PATH)); + CopyMem(parent, path, len); + CopyMem((UINT8*) parent + len, EndDevicePath, sizeof(EFI_DEVICE_PATH)); + + return parent; +} + +static VOID config_load_xbootldr( + Config *config, + EFI_HANDLE *device) { + + EFI_DEVICE_PATH *partition_path, *node, *disk_path, *copy; + UINT32 found_partition_number = (UINT32) -1; + UINT64 found_partition_start = (UINT64) -1; + UINT64 found_partition_size = (UINT64) -1; + UINT8 found_partition_signature[16] = {}; + EFI_HANDLE new_device; + EFI_FILE *root_dir; + EFI_STATUS r; + + partition_path = DevicePathFromHandle(device); + if (!partition_path) + return; + + for (node = partition_path; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { + EFI_HANDLE disk_handle; + EFI_BLOCK_IO *block_io; + EFI_DEVICE_PATH *p; + UINTN nr; + + /* First, Let's look for the SCSI/SATA/USB/… device path node, i.e. one above the media + * devices */ + if (DevicePathType(node) != MESSAGING_DEVICE_PATH) + continue; + + /* Determine the device path one level up */ + disk_path = path_parent(partition_path, node); + p = disk_path; + r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, &p, &disk_handle); + if (EFI_ERROR(r)) + continue; + + r = uefi_call_wrapper(BS->HandleProtocol, 3, disk_handle, &BlockIoProtocol, (VOID **)&block_io); + if (EFI_ERROR(r)) + continue; + + /* Filter out some block devices early. (We only care about block devices that aren't + * partitions themselves — we look for GPT partition tables to parse after all —, and only + * those which contain a medium and have at least 2 blocks.) */ + if (block_io->Media->LogicalPartition || + !block_io->Media->MediaPresent || + block_io->Media->LastBlock <= 1) + continue; + + /* Try both copies of the GPT header, in case one is corrupted */ + for (nr = 0; nr < 2; nr++) { + _cleanup_freepool_ EFI_PARTITION_ENTRY* entries = NULL; + union { + EFI_PARTITION_TABLE_HEADER gpt_header; + uint8_t space[((sizeof(EFI_PARTITION_TABLE_HEADER) + 511) / 512) * 512]; + } gpt_header_buffer; + const EFI_PARTITION_TABLE_HEADER *h = &gpt_header_buffer.gpt_header; + UINT64 where; + UINTN i, sz; + UINT32 c; + + if (nr == 0) + /* Read the first copy at LBA 1 */ + where = 1; + else + /* Read the second copy at the very last LBA of this block device */ + where = block_io->Media->LastBlock; + + /* Read the GPT header */ + r = uefi_call_wrapper(block_io->ReadBlocks, 5, + block_io, + block_io->Media->MediaId, + where, + sizeof(gpt_header_buffer), &gpt_header_buffer); + if (EFI_ERROR(r)) + continue; + + /* Some superficial validation of the GPT header */ + c = CompareMem(&h->Header.Signature, "EFI PART", sizeof(h->Header.Signature)); + if (c != 0) + continue; + + if (h->Header.HeaderSize < 92 || + h->Header.HeaderSize > 512) + continue; + + if (h->Header.Revision != 0x00010000U) + continue; + + /* Calculate CRC check */ + c = ~crc32_exclude_offset((UINT32) -1, + (const UINT8*) &gpt_header_buffer, + h->Header.HeaderSize, + OFFSETOF(EFI_PARTITION_TABLE_HEADER, Header.CRC32), + sizeof(h->Header.CRC32)); + if (c != h->Header.CRC32) + continue; + + if (h->MyLBA != where) + continue; + + if (h->SizeOfPartitionEntry < sizeof(EFI_PARTITION_ENTRY)) + continue; + + if (h->NumberOfPartitionEntries <= 0 || + h->NumberOfPartitionEntries > 1024) + continue; + + if (h->SizeOfPartitionEntry > UINTN_MAX / h->NumberOfPartitionEntries) /* overflow check */ + continue; + + /* Now load the GPT entry table */ + sz = ALIGN_TO((UINTN) h->SizeOfPartitionEntry * (UINTN) h->NumberOfPartitionEntries, 512); + entries = AllocatePool(sz); + + r = uefi_call_wrapper(block_io->ReadBlocks, 5, + block_io, + block_io->Media->MediaId, + h->PartitionEntryLBA, + sz, entries); + if (EFI_ERROR(r)) + continue; + + /* Calculate CRC of entries array, too */ + c = ~crc32((UINT32) -1, entries, sz); + if (c != h->PartitionEntryArrayCRC32) + continue; + + for (i = 0; i < h->NumberOfPartitionEntries; i++) { + EFI_PARTITION_ENTRY *entry; + + entry = (EFI_PARTITION_ENTRY*) ((UINT8*) entries + h->SizeOfPartitionEntry * i); + + if (CompareMem(&entry->PartitionTypeGUID, xbootldr_guid, 16) == 0) { + UINT64 end; + + /* Let's use memcpy(), in case the structs are not aligned (they really should be though) */ + CopyMem(&found_partition_start, &entry->StartingLBA, sizeof(found_partition_start)); + CopyMem(&end, &entry->EndingLBA, sizeof(end)); + + if (end < found_partition_start) /* Bogus? */ + continue; + + found_partition_size = end - found_partition_start + 1; + CopyMem(found_partition_signature, &entry->UniquePartitionGUID, sizeof(found_partition_signature)); + + found_partition_number = i + 1; + goto found; + } + } + + break; /* This GPT was fully valid, but we didn't find what we are looking for. This + * means there's no reason to check the second copy of the GPT header */ + } + } + + return; /* Not found */ + +found: + copy = DuplicateDevicePath(partition_path); + + /* Patch in the data we found */ + for (node = copy; !IsDevicePathEnd(node); node = NextDevicePathNode(node)) { + HARDDRIVE_DEVICE_PATH *hd; + + if (DevicePathType(node) != MEDIA_DEVICE_PATH) + continue; + + if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) + continue; + + hd = (HARDDRIVE_DEVICE_PATH*) node; + hd->PartitionNumber = found_partition_number; + hd->PartitionStart = found_partition_start; + hd->PartitionSize = found_partition_size; + CopyMem(hd->Signature, found_partition_signature, sizeof(hd->Signature)); + hd->MBRType = MBR_TYPE_EFI_PARTITION_TABLE_HEADER; + hd->SignatureType = SIGNATURE_TYPE_GUID; + } + + r = uefi_call_wrapper(BS->LocateDevicePath, 3, &BlockIoProtocol, ©, &new_device); + if (EFI_ERROR(r)) + return; + + root_dir = LibOpenRoot(new_device); + if (!root_dir) + return; + + config_entry_add_linux(config, new_device, root_dir); + config_load_entries(config, new_device, root_dir, NULL); +} + static EFI_STATUS image_start( EFI_HANDLE parent_image, const Config *config, @@ -2121,7 +2323,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { root_dir = LibOpenRoot(loaded_image->DeviceHandle); if (!root_dir) { - Print(L"Unable to open root directory: %r ", err); + Print(L"Unable to open root directory."); uefi_call_wrapper(BS->Stall, 1, 3 * 1000 * 1000); return EFI_LOAD_ERROR; } @@ -2142,11 +2344,14 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { config_load_defaults(&config, root_dir); /* scan /EFI/Linux/ directory */ - config_entry_add_linux(&config, loaded_image, root_dir); + config_entry_add_linux(&config, loaded_image->DeviceHandle, root_dir); /* scan /loader/entries/\*.conf files */ config_load_entries(&config, loaded_image->DeviceHandle, root_dir, loaded_image_path); + /* Similar, but on any XBOOTLDR partition */ + config_load_xbootldr(&config, loaded_image->DeviceHandle); + /* sort entries after version number */ config_sort_entries(&config); @@ -2163,7 +2368,10 @@ 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"auto-reboot-to-firmware-setup", 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); } diff --git a/src/boot/efi/crc32.c b/src/boot/efi/crc32.c new file mode 100644 index 000000000..46b9aeea9 --- /dev/null +++ b/src/boot/efi/crc32.c @@ -0,0 +1,142 @@ +/* This is copied from util-linux, which in turn copied in the version from Gary S. Brown */ + +/* + * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + * + * First, the polynomial itself and its table of feedback terms. The + * polynomial is + * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 + * + * Note that we take it "backwards" and put the highest-order term in + * the lowest-order bit. The X^32 term is "implied"; the LSB is the + * X^31 term, etc. The X^0 term (usually shown as "+1") results in + * the MSB being 1. + * + * Note that the usual hardware shift register implementation, which + * is what we're using (we're merely optimizing it by doing eight-bit + * chunks at a time) shifts bits into the lowest-order term. In our + * implementation, that means shifting towards the right. Why do we + * do it this way? Because the calculated CRC must be transmitted in + * order from highest-order term to lowest-order term. UARTs transmit + * characters in order from LSB to MSB. By storing the CRC this way, + * we hand it to the UART in the order low-byte to high-byte; the UART + * sends each low-bit to high-bit; and the result is transmission bit + * by bit from highest- to lowest-order term without requiring any bit + * shuffling on our part. Reception works similarly. + * + * The feedback terms table consists of 256, 32-bit entries. Notes + * + * The table can be generated at runtime if desired; code to do so + * is shown later. It might not be obvious, but the feedback + * terms simply represent the results of eight shift/xor opera- + * tions for all combinations of data and CRC register values. + * + * The values must be right-shifted by eight bits by the "updcrc" + * logic; the shift must be unsigned (bring in zeroes). On some + * hardware you could probably optimize the shift in assembler by + * using byte-swap instructions. + * polynomial $edb88320 + * + */ + +#include "crc32.h" + +static const UINT32 crc32_tab[] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +static inline UINT32 crc32_add_char(UINT32 crc, UINT8 c) { + return crc32_tab[(crc ^ c) & 0xff] ^ (crc >> 8); +} + +/* + * This a generic crc32() function, it takes seed as an argument, + * and does __not__ xor at the end. Then individual users can do + * whatever they need. + */ +UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len) { + const UINT8 *p = buf; + UINT32 crc = seed; + + while (len > 0) { + crc = crc32_add_char(crc, *p++); + len--; + } + + return crc; +} + +UINT32 crc32_exclude_offset( + UINT32 seed, + const VOID *buf, + UINTN len, + UINTN exclude_off, + UINTN exclude_len) { + + const UINT8 *p = buf; + UINT32 crc = seed; + UINTN i; + + for (i = 0; i < len; i++) { + UINT8 x = *p++; + + if (i >= exclude_off && i < exclude_off + exclude_len) + x = 0; + + crc = crc32_add_char(crc, x); + } + + return crc; +} diff --git a/src/boot/efi/crc32.h b/src/boot/efi/crc32.h new file mode 100644 index 000000000..64150ee94 --- /dev/null +++ b/src/boot/efi/crc32.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include +#include + +UINT32 crc32(UINT32 seed, const VOID *buf, UINTN len); +UINT32 crc32_exclude_offset(UINT32 seed, const VOID *buf, UINTN len, UINTN exclude_off, UINTN exclude_len); diff --git a/src/boot/efi/linux.c b/src/boot/efi/linux.c index 5b4c08588..ad26cc510 100644 --- a/src/boot/efi/linux.c +++ b/src/boot/efi/linux.c @@ -6,107 +6,54 @@ #include "linux.h" #include "util.h" -#define SETUP_MAGIC 0x53726448 /* "HdrS" */ -struct SetupHeader { - UINT8 boot_sector[0x01f1]; - UINT8 setup_secs; - UINT16 root_flags; - UINT32 sys_size; - UINT16 ram_size; - UINT16 video_mode; - UINT16 root_dev; - UINT16 signature; - UINT16 jump; - UINT32 header; - UINT16 version; - UINT16 su_switch; - UINT16 setup_seg; - UINT16 start_sys; - UINT16 kernel_ver; - UINT8 loader_id; - UINT8 load_flags; - UINT16 movesize; - UINT32 code32_start; - UINT32 ramdisk_start; - UINT32 ramdisk_len; - UINT32 bootsect_kludge; - UINT16 heap_end; - UINT8 ext_loader_ver; - UINT8 ext_loader_type; - UINT32 cmd_line_ptr; - UINT32 ramdisk_max; - UINT32 kernel_alignment; - UINT8 relocatable_kernel; - UINT8 min_alignment; - UINT16 xloadflags; - UINT32 cmdline_size; - UINT32 hardware_subarch; - UINT64 hardware_subarch_data; - UINT32 payload_offset; - UINT32 payload_length; - UINT64 setup_data; - UINT64 pref_address; - UINT32 init_size; - UINT32 handover_offset; -} __attribute__((packed)); - #ifdef __x86_64__ -typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup); -static VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { +typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params); +static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) { handover_f handover; asm volatile ("cli"); - handover = (handover_f)((UINTN)setup->code32_start + 512 + setup->handover_offset); - handover(image, ST, setup); + handover = (handover_f)((UINTN)params->hdr.code32_start + 512 + params->hdr.handover_offset); + handover(image, ST, params); } #else -typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct SetupHeader *setup) __attribute__((regparm(0))); -static VOID linux_efi_handover(EFI_HANDLE image, struct SetupHeader *setup) { +typedef VOID(*handover_f)(VOID *image, EFI_SYSTEM_TABLE *table, struct boot_params *params) __attribute__((regparm(0))); +static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) { handover_f handover; - handover = (handover_f)((UINTN)setup->code32_start + setup->handover_offset); - handover(image, ST, setup); + handover = (handover_f)((UINTN)params->hdr.code32_start + params->hdr.handover_offset); + handover(image, ST, params); } #endif EFI_STATUS linux_exec(EFI_HANDLE *image, CHAR8 *cmdline, UINTN cmdline_len, UINTN linux_addr, - UINTN initrd_addr, UINTN initrd_size, BOOLEAN secure) { - struct SetupHeader *image_setup; - struct SetupHeader *boot_setup; + UINTN initrd_addr, UINTN initrd_size) { + struct boot_params *image_params; + struct boot_params *boot_params; + UINT8 setup_sectors; EFI_PHYSICAL_ADDRESS addr; EFI_STATUS err; - image_setup = (struct SetupHeader *)(linux_addr); - if (image_setup->signature != 0xAA55 || image_setup->header != SETUP_MAGIC) + image_params = (struct boot_params *) linux_addr; + + if (image_params->hdr.boot_flag != 0xAA55 || + image_params->hdr.header != SETUP_MAGIC || + image_params->hdr.version < 0x20b || + !image_params->hdr.relocatable_kernel) return EFI_LOAD_ERROR; - if (image_setup->version < 0x20b || !image_setup->relocatable_kernel) - return EFI_LOAD_ERROR; - - addr = 0x3fffffff; + boot_params = (struct boot_params *) 0xFFFFFFFF; err = uefi_call_wrapper(BS->AllocatePages, 4, AllocateMaxAddress, EfiLoaderData, - EFI_SIZE_TO_PAGES(0x4000), &addr); + EFI_SIZE_TO_PAGES(0x4000), (UINTN *) &boot_params); if (EFI_ERROR(err)) return err; - boot_setup = (struct SetupHeader *)(UINTN)addr; - ZeroMem(boot_setup, 0x4000); - CopyMem(boot_setup, image_setup, sizeof(struct SetupHeader)); - boot_setup->loader_id = 0xff; - if (secure) { - /* set secure boot flag in linux kernel zero page, see - - Documentation/x86/zero-page.txt - - arch/x86/include/uapi/asm/bootparam.h - - drivers/firmware/efi/libstub/secureboot.c - in the linux kernel source tree - Possible values: 0 (unassigned), 1 (undetected), 2 (disabled), 3 (enabled) - */ - boot_setup->boot_sector[0x1ec] = 3; - } - - boot_setup->code32_start = (UINT32)linux_addr + (image_setup->setup_secs+1) * 512; + ZeroMem(boot_params, 0x4000); + CopyMem(&boot_params->hdr, &image_params->hdr, sizeof(struct setup_header)); + boot_params->hdr.type_of_loader = 0xff; + setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4; + boot_params->hdr.code32_start = (UINT32)linux_addr + (setup_sectors + 1) * 512; if (cmdline) { addr = 0xA0000; @@ -116,12 +63,12 @@ EFI_STATUS linux_exec(EFI_HANDLE *image, return err; CopyMem((VOID *)(UINTN)addr, cmdline, cmdline_len); ((CHAR8 *)(UINTN)addr)[cmdline_len] = 0; - boot_setup->cmd_line_ptr = (UINT32)addr; + boot_params->hdr.cmd_line_ptr = (UINT32)addr; } - boot_setup->ramdisk_start = (UINT32)initrd_addr; - boot_setup->ramdisk_len = (UINT32)initrd_size; + boot_params->hdr.ramdisk_image = (UINT32)initrd_addr; + boot_params->hdr.ramdisk_size = (UINT32)initrd_size; - linux_efi_handover(image, boot_setup); + linux_efi_handover(image, boot_params); return EFI_LOAD_ERROR; } diff --git a/src/boot/efi/linux.h b/src/boot/efi/linux.h index 2458a2fbd..ec655ce64 100644 --- a/src/boot/efi/linux.h +++ b/src/boot/efi/linux.h @@ -1,7 +1,87 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#define SETUP_MAGIC 0x53726448 /* "HdrS" */ + +struct setup_header { + UINT8 setup_sects; + UINT16 root_flags; + UINT32 syssize; + UINT16 ram_size; + UINT16 vid_mode; + UINT16 root_dev; + UINT16 boot_flag; + UINT16 jump; + UINT32 header; + UINT16 version; + UINT32 realmode_swtch; + UINT16 start_sys_seg; + UINT16 kernel_version; + UINT8 type_of_loader; + UINT8 loadflags; + UINT16 setup_move_size; + UINT32 code32_start; + UINT32 ramdisk_image; + UINT32 ramdisk_size; + UINT32 bootsect_kludge; + UINT16 heap_end_ptr; + UINT8 ext_loader_ver; + UINT8 ext_loader_type; + UINT32 cmd_line_ptr; + UINT32 initrd_addr_max; + UINT32 kernel_alignment; + UINT8 relocatable_kernel; + UINT8 min_alignment; + UINT16 xloadflags; + UINT32 cmdline_size; + UINT32 hardware_subarch; + UINT64 hardware_subarch_data; + UINT32 payload_offset; + UINT32 payload_length; + UINT64 setup_data; + UINT64 pref_address; + UINT32 init_size; + UINT32 handover_offset; +} __attribute__((packed)); + +/* adapted from linux' bootparam.h */ +struct boot_params { + UINT8 screen_info[64]; // was: struct screen_info + UINT8 apm_bios_info[20]; // was: struct apm_bios_info + UINT8 _pad2[4]; + UINT64 tboot_addr; + UINT8 ist_info[16]; // was: struct ist_info + UINT8 _pad3[16]; + UINT8 hd0_info[16]; + UINT8 hd1_info[16]; + UINT8 sys_desc_table[16]; // was: struct sys_desc_table + UINT8 olpc_ofw_header[16]; // was: struct olpc_ofw_header + UINT32 ext_ramdisk_image; + UINT32 ext_ramdisk_size; + UINT32 ext_cmd_line_ptr; + UINT8 _pad4[116]; + UINT8 edid_info[128]; // was: struct edid_info + UINT8 efi_info[32]; // was: struct efi_info + UINT32 alt_mem_k; + UINT32 scratch; + UINT8 e820_entries; + UINT8 eddbuf_entries; + UINT8 edd_mbr_sig_buf_entries; + UINT8 kbd_status; + UINT8 secure_boot; + UINT8 _pad5[2]; + UINT8 sentinel; + UINT8 _pad6[1]; + struct setup_header hdr; + UINT8 _pad7[0x290-0x1f1-sizeof(struct setup_header)]; + UINT32 edd_mbr_sig_buffer[16]; // was: edd_mbr_sig_buffer[EDD_MBR_SIG_MAX] + UINT8 e820_table[20*128]; // was: struct boot_e820_entry e820_table[E820_MAX_ENTRIES_ZEROPAGE] + UINT8 _pad8[48]; + UINT8 eddbuf[6*82]; // was: struct edd_info eddbuf[EDDMAXNR] + UINT8 _pad9[276]; +} __attribute__((packed)); + EFI_STATUS linux_exec(EFI_HANDLE *image, CHAR8 *cmdline, UINTN cmdline_size, UINTN linux_addr, - UINTN initrd_addr, UINTN initrd_size, BOOLEAN secure); + UINTN initrd_addr, UINTN initrd_size); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 214015184..0ae319163 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -2,14 +2,15 @@ efi_headers = files(''' console.h + crc32.h disk.h graphics.h linux.h measure.h pe.h + shim.h splash.h util.h - shim.h '''.split()) common_sources = ''' @@ -24,6 +25,7 @@ systemd_boot_sources = ''' boot.c console.c shim.c + crc32.c '''.split() stub_sources = ''' diff --git a/src/boot/efi/stub.c b/src/boot/efi/stub.c index 6b0787997..26c204fb4 100644 --- a/src/boot/efi/stub.c +++ b/src/boot/efi/stub.c @@ -87,12 +87,13 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { #endif } - /* export the device path this image is started from */ - if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) - efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); + /* Export the device path this image is started from, if it's not set yet */ + if (efivar_get_raw(&loader_guid, L"LoaderDevicePartUUID", NULL, NULL) != EFI_SUCCESS) + if (disk_get_part_uuid(loaded_image->DeviceHandle, uuid) == EFI_SUCCESS) + efivar_set(L"LoaderDevicePartUUID", uuid, FALSE); /* if LoaderImageIdentifier is not set, assume the image with this stub was loaded directly from UEFI */ - if (efivar_get_raw(&global_guid, L"LoaderImageIdentifier", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderImageIdentifier", NULL, NULL) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = DevicePathToStr(loaded_image->FilePath); @@ -100,7 +101,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* if LoaderFirmwareInfo is not set, let's set it */ - if (efivar_get_raw(&global_guid, L"LoaderFirmwareInfo", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderFirmwareInfo", NULL, NULL) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = PoolPrint(L"%s %d.%02d", ST->FirmwareVendor, ST->FirmwareRevision >> 16, ST->FirmwareRevision & 0xffff); @@ -108,7 +109,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* ditto for LoaderFirmwareType */ - if (efivar_get_raw(&global_guid, L"LoaderFirmwareType", &b, &size) != EFI_SUCCESS) { + if (efivar_get_raw(&loader_guid, L"LoaderFirmwareType", NULL, NULL) != EFI_SUCCESS) { _cleanup_freepool_ CHAR16 *s; s = PoolPrint(L"UEFI %d.%02d", ST->Hdr.Revision >> 16, ST->Hdr.Revision & 0xffff); @@ -116,7 +117,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { } /* add StubInfo */ - if (efivar_get_raw(&global_guid, L"StubInfo", &b, &size) != EFI_SUCCESS) + if (efivar_get_raw(&loader_guid, L"StubInfo", NULL, NULL) != EFI_SUCCESS) efivar_set(L"StubInfo", L"systemd-stub " GIT_VERSION, FALSE); if (szs[3] > 0) @@ -124,7 +125,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { err = linux_exec(image, cmdline, cmdline_len, (UINTN)loaded_image->ImageBase + addrs[1], - (UINTN)loaded_image->ImageBase + addrs[2], szs[2], secure); + (UINTN)loaded_image->ImageBase + addrs[2], szs[2]); graphics_mode(FALSE); Print(L"Execution of embedded linux image failed: %r\n", err); diff --git a/src/boot/efi/util.c b/src/boot/efi/util.c index f1f1674c5..4134e2200 100644 --- a/src/boot/efi/util.c +++ b/src/boot/efi/util.c @@ -117,10 +117,12 @@ EFI_STATUS efivar_get(const CHAR16 *name, CHAR16 **value) { if ((size % 2) != 0) return EFI_INVALID_PARAMETER; + if (!value) + return EFI_SUCCESS; + /* 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; + *value = (CHAR16*) TAKE_PTR(buf); return EFI_SUCCESS; } @@ -141,7 +143,7 @@ EFI_STATUS efivar_get_int(const CHAR16 *name, UINTN *i) { EFI_STATUS err; err = efivar_get(name, &val); - if (!EFI_ERROR(err)) + if (!EFI_ERROR(err) && i) *i = Atoi(val); return err; @@ -159,8 +161,10 @@ EFI_STATUS efivar_get_raw(const EFI_GUID *vendor, const CHAR16 *name, CHAR8 **bu err = uefi_call_wrapper(RT->GetVariable, 5, (CHAR16*) name, (EFI_GUID *)vendor, NULL, &l, buf); if (!EFI_ERROR(err)) { - *buffer = buf; - buf = NULL; + + if (buffer) + *buffer = TAKE_PTR(buf); + if (size) *size = l; } diff --git a/src/boot/efi/util.h b/src/boot/efi/util.h index 4ba7050a9..cef127f40 100644 --- a/src/boot/efi/util.h +++ b/src/boot/efi/util.h @@ -7,6 +7,10 @@ #define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) #define OFFSETOF(x,y) __builtin_offsetof(x,y) +static inline UINTN ALIGN_TO(UINTN l, UINTN ali) { + return ((l + ali - 1) & ~(ali - 1)); +} + static inline const CHAR16 *yes_no(BOOLEAN b) { return b ? L"yes" : L"no"; } @@ -55,3 +59,10 @@ const EFI_GUID loader_guid; #define UINTN_MAX (~(UINTN)0) #define INTN_MAX ((INTN)(UINTN_MAX>>1)) + +#define TAKE_PTR(ptr) \ + ({ \ + typeof(ptr) _ptr_ = (ptr); \ + (ptr) = NULL; \ + _ptr_; \ + }) diff --git a/src/busctl/busctl.c b/src/busctl/busctl.c index 08d9e70c9..c8125c660 100644 --- a/src/busctl/busctl.c +++ b/src/busctl/busctl.c @@ -25,10 +25,10 @@ #include "path-util.h" #include "pretty-print.h" #include "set.h" +#include "sort-util.h" #include "strv.h" #include "terminal-util.h" #include "user-util.h" -#include "util.h" #include "verbs.h" static enum { @@ -57,6 +57,7 @@ static bool arg_allow_interactive_authorization = true; static bool arg_augment_creds = true; static bool arg_watch_bind = false; static usec_t arg_timeout = 0; +static const char *arg_destination = NULL; STATIC_DESTRUCTOR_REGISTER(arg_matches, strv_freep); @@ -795,10 +796,8 @@ static int on_interface(const char *interface, uint64_t flags, void *userdata) { return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate interface"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate interface"); m = NULL; return 0; @@ -836,10 +835,8 @@ static int on_method(const char *interface, const char *name, const char *signat return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate method"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate method"); m = NULL; return 0; @@ -873,10 +870,8 @@ static int on_signal(const char *interface, const char *name, const char *signat return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate signal"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate signal"); m = NULL; return 0; @@ -911,10 +906,8 @@ static int on_property(const char *interface, const char *name, const char *sign return log_oom(); r = set_put(members, m); - if (r <= 0) { - log_error("Duplicate property"); - return -EINVAL; - } + if (r <= 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Duplicate property"); m = NULL; return 0; @@ -1195,10 +1188,8 @@ static int monitor(int argc, char **argv, int (*dump)(sd_bus_message *m, FILE *f STRV_FOREACH(i, argv+1) { _cleanup_free_ char *m = NULL; - if (!service_name_is_valid(*i)) { - log_error("Invalid service name '%s'", *i); - return -EINVAL; - } + if (!service_name_is_valid(*i)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid service name '%s'", *i); m = strjoin("sender='", *i, "'"); if (!m) @@ -1978,10 +1969,8 @@ static int call(int argc, char **argv, void *userdata) { if (r < 0) return r; - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } + if (*p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); } if (!arg_expect_reply) { @@ -2036,6 +2025,49 @@ static int call(int argc, char **argv, void *userdata) { return 0; } +static int emit_signal(int argc, char **argv, void *userdata) { + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; + int r; + + r = acquire_bus(false, &bus); + if (r < 0) + return r; + + r = sd_bus_message_new_signal(bus, &m, argv[1], argv[2], argv[3]); + if (r < 0) + return bus_log_create_error(r); + + if (arg_destination) { + r = sd_bus_message_set_destination(m, arg_destination); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_set_auto_start(m, arg_auto_start); + if (r < 0) + return bus_log_create_error(r); + + if (!isempty(argv[4])) { + char **p; + + p = argv+5; + + r = message_append_cmdline(m, argv[4], &p); + if (r < 0) + return r; + + if (*p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); + } + + r = sd_bus_send(bus, m, NULL); + if (r < 0) + return log_error_errno(r, "Failed to send signal: %m"); + + return 0; +} + static int get_property(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; @@ -2132,10 +2164,8 @@ static int set_property(int argc, char **argv, void *userdata) { if (r < 0) return bus_log_create_error(r); - if (*p) { - log_error("Too many parameters for signature."); - return -EINVAL; - } + if (*p) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Too many parameters for signature."); r = sd_bus_call(bus, m, arg_timeout, &error, NULL); if (r < 0) @@ -2154,48 +2184,51 @@ static int help(void) { printf("%s [OPTIONS...] {COMMAND} ...\n\n" "Introspect the bus.\n\n" - " -h --help Show this help\n" - " --version Show package version\n" - " --no-pager Do not pipe output into a pager\n" - " --no-legend Do not show the headers and footers\n" - " --system Connect to system bus\n" - " --user Connect to user bus\n" - " -H --host=[USER@]HOST Operate on remote host\n" - " -M --machine=CONTAINER Operate on local container\n" - " --address=ADDRESS Connect to bus specified by address\n" - " --show-machine Show machine ID column in list\n" - " --unique Only show unique names\n" - " --acquired Only show acquired names\n" - " --activatable Only show activatable names\n" - " --match=MATCH Only show matching messages\n" - " --size=SIZE Maximum length of captured packet\n" - " --list Don't show tree, but simple object path list\n" - " -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" + " -h --help Show this help\n" + " --version Show package version\n" + " --no-pager Do not pipe output into a pager\n" + " --no-legend Do not show the headers and footers\n" + " --system Connect to system bus\n" + " --user Connect to user bus\n" + " -H --host=[USER@]HOST Operate on remote host\n" + " -M --machine=CONTAINER Operate on local container\n" + " --address=ADDRESS Connect to bus specified by address\n" + " --show-machine Show machine ID column in list\n" + " --unique Only show unique names\n" + " --acquired Only show acquired names\n" + " --activatable Only show activatable names\n" + " --match=MATCH Only show matching messages\n" + " --size=SIZE Maximum length of captured packet\n" + " --list Don't show tree, but simple object path list\n" + " -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" - " Allow interactive authorization for operation\n" - " --timeout=SECS Maximum time to wait for method call completion\n" - " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n" - " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n" - " system\n\n" - "Commands:\n" - " list List bus names\n" - " status [SERVICE] Show bus service, process or bus owner credentials\n" - " monitor [SERVICE...] Show bus traffic\n" - " capture [SERVICE...] Capture bus traffic as pcap\n" - " tree [SERVICE...] Show object tree of service\n" + " Allow interactive authorization for operation\n" + " --timeout=SECS Maximum time to wait for method call completion\n" + " --augment-creds=BOOL Extend credential data with data read from /proc/$PID\n" + " --watch-bind=BOOL Wait for bus AF_UNIX socket to be bound in the file\n" + " system\n" + " --destination=SERVICE Destination service of a signal\n" + "\nCommands:\n" + " list List bus names\n" + " status [SERVICE] Show bus service, process or bus owner credentials\n" + " monitor [SERVICE...] Show bus traffic\n" + " capture [SERVICE...] Capture bus traffic as pcap\n" + " tree [SERVICE...] Show object tree of service\n" " introspect SERVICE OBJECT [INTERFACE]\n" " call SERVICE OBJECT INTERFACE METHOD [SIGNATURE [ARGUMENT...]]\n" - " Call a method\n" + " Call a method\n" + " emit OBJECT INTERFACE SIGNAL [SIGNATURE [ARGUMENT...]]\n" + " Emit a signal\n" " get-property SERVICE OBJECT INTERFACE PROPERTY...\n" - " Get property value\n" + " Get property value\n" " set-property SERVICE OBJECT INTERFACE PROPERTY SIGNATURE ARGUMENT...\n" - " Set property value\n" - " help Show this help\n" + " Set property value\n" + " help Show this help\n" "\nSee the %s for details.\n" , program_invocation_short_name , link @@ -2232,6 +2265,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_AUGMENT_CREDS, ARG_WATCH_BIND, ARG_JSON, + ARG_DESTINATION, }; static const struct option options[] = { @@ -2260,6 +2294,7 @@ static int parse_argv(int argc, char *argv[]) { { "augment-creds", required_argument, NULL, ARG_AUGMENT_CREDS }, { "watch-bind", required_argument, NULL, ARG_WATCH_BIND }, { "json", required_argument, NULL, ARG_JSON }, + { "destination", required_argument, NULL, ARG_DESTINATION }, {}, }; @@ -2426,6 +2461,10 @@ static int parse_argv(int argc, char *argv[]) { break; + case ARG_DESTINATION: + arg_destination = optarg; + break; + case '?': return -EINVAL; @@ -2446,6 +2485,7 @@ static int busctl_main(int argc, char *argv[]) { { "tree", VERB_ANY, VERB_ANY, 0, tree }, { "introspect", 3, 4, 0, introspect }, { "call", 5, VERB_ANY, 0, call }, + { "emit", 4, VERB_ANY, 0, emit_signal }, { "get-property", 5, VERB_ANY, 0, get_property }, { "set-property", 6, VERB_ANY, 0, set_property }, { "help", VERB_ANY, VERB_ANY, 0, verb_help }, diff --git a/src/cgls/cgls.c b/src/cgls/cgls.c index b6b15cf11..09ee0e86a 100644 --- a/src/cgls/cgls.c +++ b/src/cgls/cgls.c @@ -53,8 +53,8 @@ static int help(void) { " --version Show package version\n" " --no-pager Do not pipe output into a pager\n" " -a --all Show all groups, including empty\n" - " -u --unit Show the subtrees of specifified system units\n" - " --user-unit Show the subtrees of specifified user units\n" + " -u --unit Show the subtrees of specified system units\n" + " --user-unit Show the subtrees of specified user units\n" " -l --full Do not ellipsize output\n" " -k Include kernel threads in output\n" " -M --machine= Show container\n" diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index b3bda30ce..f27c80120 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -25,11 +25,11 @@ #include "pretty-print.h" #include "process-util.h" #include "procfs-util.h" +#include "sort-util.h" #include "stdio-util.h" #include "strv.h" #include "terminal-util.h" #include "unit-name.h" -#include "util.h" #include "virt.h" typedef struct Group { @@ -223,71 +223,6 @@ static int process( if (g->n_tasks > 0) g->n_tasks_valid = true; - } 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; - - if (is_root_cgroup(path)) { - r = procfs_cpu_get_usage(&new_usage); - if (r < 0) - return r; - } else if (all_unified) { - _cleanup_free_ char *val = NULL; - - if (!streq(controller, "cpu")) - return 0; - - r = cg_get_keyed_attribute("cpu", path, "cpu.stat", STRV_MAKE("usage_usec"), &val); - if (IN_SET(r, -ENOENT, -ENXIO)) - return 0; - if (r < 0) - return r; - - r = safe_atou64(val, &new_usage); - if (r < 0) - return r; - - new_usage *= NSEC_PER_USEC; - } else { - if (!streq(controller, "cpuacct")) - return 0; - - r = cg_get_path(controller, path, "cpuacct.usage", &p); - if (r < 0) - return r; - - r = read_one_line_file(p, &v); - if (r == -ENOENT) - return 0; - if (r < 0) - return r; - - r = safe_atou64(v, &new_usage); - if (r < 0) - return r; - } - - timestamp = now_nsec(CLOCK_MONOTONIC); - - if (g->cpu_iteration == iteration - 1 && - (nsec_t) new_usage > g->cpu_usage) { - - nsec_t x, y; - - x = timestamp - g->cpu_timestamp; - if (x < 1) - x = 1; - - y = (nsec_t) new_usage - g->cpu_usage; - g->cpu_fraction = (double) y / (double) x; - g->cpu_valid = true; - } - - g->cpu_usage = (nsec_t) new_usage; - g->cpu_timestamp = timestamp; - g->cpu_iteration = iteration; - } else if (streq(controller, "memory")) { if (is_root_cgroup(path)) { @@ -411,6 +346,71 @@ static int process( g->io_output = wr; g->io_timestamp = timestamp; g->io_iteration = iteration; + } 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; + + if (is_root_cgroup(path)) { + r = procfs_cpu_get_usage(&new_usage); + if (r < 0) + return r; + } else if (all_unified) { + _cleanup_free_ char *val = NULL; + + if (!streq(controller, "cpu")) + return 0; + + r = cg_get_keyed_attribute("cpu", path, "cpu.stat", STRV_MAKE("usage_usec"), &val); + if (IN_SET(r, -ENOENT, -ENXIO)) + return 0; + if (r < 0) + return r; + + r = safe_atou64(val, &new_usage); + if (r < 0) + return r; + + new_usage *= NSEC_PER_USEC; + } else { + if (!streq(controller, "cpuacct")) + return 0; + + r = cg_get_path(controller, path, "cpuacct.usage", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &new_usage); + if (r < 0) + return r; + } + + timestamp = now_nsec(CLOCK_MONOTONIC); + + if (g->cpu_iteration == iteration - 1 && + (nsec_t) new_usage > g->cpu_usage) { + + nsec_t x, y; + + x = timestamp - g->cpu_timestamp; + if (x < 1) + x = 1; + + y = (nsec_t) new_usage - g->cpu_usage; + g->cpu_fraction = (double) y / (double) x; + g->cpu_valid = true; + } + + g->cpu_usage = (nsec_t) new_usage; + g->cpu_timestamp = timestamp; + g->cpu_iteration = iteration; + } if (ret) @@ -922,10 +922,9 @@ static int run(int argc, char *argv[]) { arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; - if (arg_recursive_unset && arg_count == COUNT_PIDS) { - log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); - return -EINVAL; - } + if (arg_recursive_unset && arg_count == COUNT_PIDS) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); r = show_cgroup_get_path_and_warn(arg_machine, arg_root, &root); if (r < 0) diff --git a/src/core/automount.c b/src/core/automount.c index 6a8373920..bcdd7e19a 100644 --- a/src/core/automount.c +++ b/src/core/automount.c @@ -759,7 +759,7 @@ static void automount_enter_running(Automount *a) { return; } - mkdir_p_label(a->where, a->directory_mode); + (void) mkdir_p_label(a->where, a->directory_mode); /* Before we do anything, let's see if somebody is playing games with us? */ if (lstat(a->where, &st) < 0) { @@ -781,7 +781,7 @@ static void automount_enter_running(Automount *a) { goto fail; } - r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(a)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue mount startup job: %s", bus_error_message(&error, r)); goto fail; @@ -796,7 +796,6 @@ fail: static int automount_start(Unit *u) { Automount *a = AUTOMOUNT(u); - Unit *trigger; int r; assert(a); @@ -807,13 +806,11 @@ static int automount_start(Unit *u) { return -EEXIST; } - trigger = UNIT_TRIGGER(u); - if (!trigger || trigger->load_state != UNIT_LOADED) { - log_unit_error(u, "Refusing to start, unit to trigger not loaded."); - return -ENOENT; - } + r = unit_test_trigger_loaded(u); + if (r < 0) + return r; - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { automount_enter_dead(a, AUTOMOUNT_FAILURE_START_LIMIT_HIT); return r; @@ -1038,7 +1035,7 @@ static int automount_dispatch_io(sd_event_source *s, int fd, uint32_t events, vo goto fail; } - r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(a)->manager, JOB_STOP, trigger, JOB_REPLACE, NULL, &error, NULL); if (r < 0) { log_unit_warning(UNIT(a), "Failed to queue umount startup job: %s", bus_error_message(&error, r)); goto fail; diff --git a/src/core/bpf-firewall.c b/src/core/bpf-firewall.c index b9a611fd9..2ecce300d 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 "memory-util.h" #include "missing_syscall.h" #include "unit.h" diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 18d470b6d..ad67ba043 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -5,15 +5,16 @@ #include "alloc-util.h" #include "blockdev-util.h" +#include "bpf-devices.h" #include "bpf-firewall.h" #include "btrfs-util.h" -#include "bpf-devices.h" #include "bus-error.h" #include "cgroup-util.h" #include "cgroup.h" #include "fd-util.h" #include "fileio.h" #include "fs-util.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -25,7 +26,7 @@ #include "string-util.h" #include "virt.h" -#define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) +#define CGROUP_CPU_QUOTA_DEFAULT_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) /* 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 @@ -98,6 +99,7 @@ void cgroup_context_init(CGroupContext *c) { .cpu_weight = CGROUP_WEIGHT_INVALID, .startup_cpu_weight = CGROUP_WEIGHT_INVALID, .cpu_quota_per_sec_usec = USEC_INFINITY, + .cpu_quota_period_usec = USEC_INFINITY, .cpu_shares = CGROUP_CPU_SHARES_INVALID, .startup_cpu_shares = CGROUP_CPU_SHARES_INVALID, @@ -206,6 +208,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { CGroupDeviceAllow *a; IPAddressAccessItem *iaai; char u[FORMAT_TIMESPAN_MAX]; + char v[FORMAT_TIMESPAN_MAX]; assert(c); assert(f); @@ -224,6 +227,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sCPUShares=%" PRIu64 "\n" "%sStartupCPUShares=%" PRIu64 "\n" "%sCPUQuotaPerSecSec=%s\n" + "%sCPUQuotaPeriodSec=%s\n" "%sIOWeight=%" PRIu64 "\n" "%sStartupIOWeight=%" PRIu64 "\n" "%sBlockIOWeight=%" PRIu64 "\n" @@ -248,6 +252,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { prefix, c->cpu_shares, prefix, c->startup_cpu_shares, prefix, format_timespan(u, sizeof(u), c->cpu_quota_per_sec_usec, 1), + prefix, format_timespan(v, sizeof(v), c->cpu_quota_period_usec, 1), prefix, c->io_weight, prefix, c->startup_io_weight, prefix, c->blockio_weight, @@ -656,6 +661,40 @@ static uint64_t cgroup_context_cpu_shares(CGroupContext *c, ManagerState state) return CGROUP_CPU_SHARES_DEFAULT; } +usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution, usec_t max_period) { + /* kernel uses a minimum resolution of 1ms, so both period and (quota * period) + * need to be higher than that boundary. quota is specified in USecPerSec. + * Additionally, period must be at most max_period. */ + assert(quota > 0); + + return MIN(MAX3(period, resolution, resolution * USEC_PER_SEC / quota), max_period); +} + +static usec_t cgroup_cpu_adjust_period_and_log(Unit *u, usec_t period, usec_t quota) { + usec_t new_period; + + if (quota == USEC_INFINITY) + /* Always use default period for infinity quota. */ + return CGROUP_CPU_QUOTA_DEFAULT_PERIOD_USEC; + + if (period == USEC_INFINITY) + /* Default period was requested. */ + period = CGROUP_CPU_QUOTA_DEFAULT_PERIOD_USEC; + + /* Clamp to interval [1ms, 1s] */ + new_period = cgroup_cpu_adjust_period(period, quota, USEC_PER_MSEC, USEC_PER_SEC); + + if (new_period != period) { + char v[FORMAT_TIMESPAN_MAX]; + log_unit_full(u, u->warned_clamping_cpu_quota_period ? LOG_DEBUG : LOG_WARNING, 0, + "Clamping CPU interval for cpu.max: period is now %s", + format_timespan(v, sizeof(v), new_period, 1)); + u->warned_clamping_cpu_quota_period = true; + } + + return new_period; +} + static void cgroup_apply_unified_cpu_weight(Unit *u, uint64_t weight) { char buf[DECIMAL_STR_MAX(uint64_t) + 2]; @@ -663,14 +702,15 @@ static void cgroup_apply_unified_cpu_weight(Unit *u, uint64_t weight) { (void) set_attribute_and_warn(u, "cpu", "cpu.weight", buf); } -static void cgroup_apply_unified_cpu_quota(Unit *u, usec_t quota) { +static void cgroup_apply_unified_cpu_quota(Unit *u, usec_t quota, usec_t period) { char buf[(DECIMAL_STR_MAX(usec_t) + 1) * 2 + 1]; + period = cgroup_cpu_adjust_period_and_log(u, period, quota); 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); + MAX(quota * period / USEC_PER_SEC, USEC_PER_MSEC), period); else - xsprintf(buf, "max " USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); + xsprintf(buf, "max " USEC_FMT "\n", period); (void) set_attribute_and_warn(u, "cpu", "cpu.max", buf); } @@ -681,14 +721,16 @@ static void cgroup_apply_legacy_cpu_shares(Unit *u, uint64_t shares) { (void) set_attribute_and_warn(u, "cpu", "cpu.shares", buf); } -static void cgroup_apply_legacy_cpu_quota(Unit *u, usec_t quota) { +static void cgroup_apply_legacy_cpu_quota(Unit *u, usec_t quota, usec_t period) { char buf[DECIMAL_STR_MAX(usec_t) + 2]; - xsprintf(buf, USEC_FMT "\n", CGROUP_CPU_QUOTA_PERIOD_USEC); + period = cgroup_cpu_adjust_period_and_log(u, period, quota); + + xsprintf(buf, USEC_FMT "\n", period); (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); + xsprintf(buf, USEC_FMT "\n", MAX(quota * period / USEC_PER_SEC, USEC_PER_MSEC)); (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_quota_us", buf); } else (void) set_attribute_and_warn(u, "cpu", "cpu.cfs_quota_us", "-1\n"); @@ -911,7 +953,7 @@ static void cgroup_context_apply( weight = CGROUP_WEIGHT_DEFAULT; cgroup_apply_unified_cpu_weight(u, weight); - cgroup_apply_unified_cpu_quota(u, c->cpu_quota_per_sec_usec); + cgroup_apply_unified_cpu_quota(u, c->cpu_quota_per_sec_usec, c->cpu_quota_period_usec); } else { uint64_t shares; @@ -930,7 +972,7 @@ static void cgroup_context_apply( shares = CGROUP_CPU_SHARES_DEFAULT; cgroup_apply_legacy_cpu_shares(u, shares); - cgroup_apply_legacy_cpu_quota(u, c->cpu_quota_per_sec_usec); + cgroup_apply_legacy_cpu_quota(u, c->cpu_quota_per_sec_usec, c->cpu_quota_period_usec); } } @@ -2227,7 +2269,7 @@ void unit_prune_cgroup(Unit *u) { int unit_search_main_pid(Unit *u, pid_t *ret) { _cleanup_fclose_ FILE *f = NULL; - pid_t pid = 0, npid, mypid; + pid_t pid = 0, npid; int r; assert(u); @@ -2240,15 +2282,12 @@ int unit_search_main_pid(Unit *u, pid_t *ret) { if (r < 0) return r; - mypid = getpid_cached(); while (cg_read_pid(f, &npid) > 0) { - pid_t ppid; if (npid == pid) continue; - /* Ignore processes that aren't our kids */ - if (get_process_ppid(npid, &ppid) >= 0 && ppid != mypid) + if (pid_is_my_child(npid) == 0) continue; if (pid != 0) @@ -2280,7 +2319,7 @@ static int unit_watch_pids_in_path(Unit *u, const char *path) { pid_t pid; while ((r = cg_read_pid(f, &pid)) > 0) { - r = unit_watch_pid(u, pid); + r = unit_watch_pid(u, pid, false); if (r < 0 && ret >= 0) ret = r; } diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 266daa20a..51e7c96d6 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -79,10 +79,15 @@ struct CGroupContext { bool tasks_accounting; bool ip_accounting; + bool delegate; + CGroupMask delegate_controllers; + CGroupMask disable_controllers; + /* For unified hierarchy */ uint64_t cpu_weight; uint64_t startup_cpu_weight; usec_t cpu_quota_per_sec_usec; + usec_t cpu_quota_period_usec; uint64_t io_weight; uint64_t startup_io_weight; @@ -115,11 +120,6 @@ struct CGroupContext { /* Common */ uint64_t tasks_max; - - bool delegate; - CGroupMask delegate_controllers; - - CGroupMask disable_controllers; }; /* Used when querying IP accounting data */ @@ -135,6 +135,8 @@ typedef enum CGroupIPAccountingMetric { typedef struct Unit Unit; typedef struct Manager Manager; +usec_t cgroup_cpu_adjust_period(usec_t period, usec_t quota, usec_t resolution, usec_t max_period); + void cgroup_context_init(CGroupContext *c); void cgroup_context_done(CGroupContext *c); void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix); diff --git a/src/core/chown-recursive.c b/src/core/chown-recursive.c index 7767301f7..49d96b737 100644 --- a/src/core/chown-recursive.c +++ b/src/core/chown-recursive.c @@ -13,7 +13,13 @@ #include "strv.h" #include "user-util.h" -static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) { +static int chown_one( + int fd, + const struct stat *st, + uid_t uid, + gid_t gid, + mode_t mask) { + char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1]; const char *n; @@ -42,13 +48,19 @@ static int chown_one(int fd, const struct stat *st, uid_t uid, gid_t gid) { * 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) + if (chmod(procfs_path, st->st_mode & 07777 & mask) < 0) return -errno; return 1; } -static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gid_t gid) { +static int chown_recursive_internal( + int fd, + const struct stat *st, + uid_t uid, + gid_t gid, + mode_t mask) { + _cleanup_closedir_ DIR *d = NULL; bool changed = false; struct dirent *de; @@ -87,13 +99,13 @@ static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gi if (subdir_fd < 0) return subdir_fd; - r = chown_recursive_internal(subdir_fd, &fst, uid, gid); /* takes possession of subdir_fd even on failure */ + r = chown_recursive_internal(subdir_fd, &fst, uid, gid, mask); /* 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); + r = chown_one(path_fd, &fst, uid, gid, mask); if (r < 0) return r; if (r > 0) @@ -101,14 +113,19 @@ static int chown_recursive_internal(int fd, const struct stat *st, uid_t uid, gi } } - r = chown_one(dirfd(d), st, uid, gid); + r = chown_one(dirfd(d), st, uid, gid, mask); if (r < 0) return r; return r > 0 || changed; } -int path_chown_recursive(const char *path, uid_t uid, gid_t gid) { +int path_chown_recursive( + const char *path, + uid_t uid, + gid_t gid, + mode_t mask) { + _cleanup_close_ int fd = -1; struct stat st; @@ -129,5 +146,5 @@ int path_chown_recursive(const char *path, uid_t uid, gid_t gid) { (!gid_is_valid(gid) || st.st_gid == gid)) return 0; - return chown_recursive_internal(TAKE_FD(fd), &st, uid, gid); /* we donate the fd to the call, regardless if it succeeded or failed */ + return chown_recursive_internal(TAKE_FD(fd), &st, uid, gid, mask); /* we donate the fd to the call, regardless if it succeeded or failed */ } diff --git a/src/core/chown-recursive.h b/src/core/chown-recursive.h index f3fa40a65..bfee05f3b 100644 --- a/src/core/chown-recursive.h +++ b/src/core/chown-recursive.h @@ -3,4 +3,4 @@ #include -int path_chown_recursive(const char *path, uid_t uid, gid_t gid); +int path_chown_recursive(const char *path, uid_t uid, gid_t gid, mode_t mask); diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index 53890bcaf..4615aeaf6 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -13,6 +13,7 @@ #include "dbus-util.h" #include "fd-util.h" #include "fileio.h" +#include "limits-util.h" #include "path-util.h" static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy); @@ -330,6 +331,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_PROPERTY("CPUShares", "t", NULL, offsetof(CGroupContext, cpu_shares), 0), SD_BUS_PROPERTY("StartupCPUShares", "t", NULL, offsetof(CGroupContext, startup_cpu_shares), 0), SD_BUS_PROPERTY("CPUQuotaPerSecUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_per_sec_usec), 0), + SD_BUS_PROPERTY("CPUQuotaPeriodUSec", "t", bus_property_get_usec, offsetof(CGroupContext, cpu_quota_period_usec), 0), SD_BUS_PROPERTY("IOAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, io_accounting), 0), SD_BUS_PROPERTY("IOWeight", "t", NULL, offsetof(CGroupContext, io_weight), 0), SD_BUS_PROPERTY("StartupIOWeight", "t", NULL, offsetof(CGroupContext, startup_io_weight), 0), @@ -713,6 +715,7 @@ int bus_cgroup_set_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { c->cpu_quota_per_sec_usec = u64; + u->warned_clamping_cpu_quota_period = false; unit_invalidate_cgroup(u, CGROUP_MASK_CPU); if (c->cpu_quota_per_sec_usec == USEC_INFINITY) @@ -727,6 +730,29 @@ int bus_cgroup_set_property( return 1; + } else if (streq(name, "CPUQuotaPeriodUSec")) { + uint64_t u64; + + r = sd_bus_message_read(message, "t", &u64); + if (r < 0) + return r; + + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { + c->cpu_quota_period_usec = u64; + u->warned_clamping_cpu_quota_period = false; + unit_invalidate_cgroup(u, CGROUP_MASK_CPU); + if (c->cpu_quota_period_usec == USEC_INFINITY) + unit_write_setting(u, flags, "CPUQuotaPeriodSec", "CPUQuotaPeriodSec="); + else { + char v[FORMAT_TIMESPAN_MAX]; + unit_write_settingf(u, flags, "CPUQuotaPeriodSec", + "CPUQuotaPeriodSec=%s", + format_timespan(v, sizeof(v), c->cpu_quota_period_usec, 1)); + } + } + + return 1; + } else if ((iol_type = cgroup_io_limit_type_from_string(name)) >= 0) { const char *path; unsigned n = 0; diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c index 11301e4b6..5532d1ada 100644 --- a/src/core/dbus-execute.c +++ b/src/core/dbus-execute.c @@ -771,12 +771,15 @@ const sd_bus_vtable bus_exec_vtable[] = { SD_BUS_PROPERTY("ConfigurationDirectory", "as", NULL, offsetof(ExecContext, directories[EXEC_DIRECTORY_CONFIGURATION].paths), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MemoryDenyWriteExecute", "b", bus_property_get_bool, offsetof(ExecContext, memory_deny_write_execute), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RestrictRealtime", "b", bus_property_get_bool, offsetof(ExecContext, restrict_realtime), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("RestrictSUIDSGID", "b", bus_property_get_bool, offsetof(ExecContext, restrict_suid_sgid), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("RestrictNamespaces", "t", bus_property_get_ulong, offsetof(ExecContext, restrict_namespaces), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("BindReadOnlyPaths", "a(ssbt)", property_get_bind_paths, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TemporaryFileSystem", "a(ss)", property_get_temporary_filesystems, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("MountAPIVFS", "b", bus_property_get_bool, offsetof(ExecContext, mount_apivfs), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("KeyringMode", "s", property_get_exec_keyring_mode, offsetof(ExecContext, keyring_mode), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("ProtectHostname", "b", bus_property_get_bool, offsetof(ExecContext, protect_hostname), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("NetworkNamespacePath", "s", NULL, offsetof(ExecContext, network_namespace_path), SD_BUS_VTABLE_PROPERTY_CONST), /* Obsolete/redundant properties: */ SD_BUS_PROPERTY("Capabilities", "s", property_get_empty_string, 0, SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_HIDDEN), @@ -1126,6 +1129,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "RestrictRealtime")) return bus_set_transient_bool(u, name, &c->restrict_realtime, message, flags, error); + if (streq(name, "RestrictSUIDSGID")) + return bus_set_transient_bool(u, name, &c->restrict_suid_sgid, message, flags, error); + if (streq(name, "DynamicUser")) return bus_set_transient_bool(u, name, &c->dynamic_user, message, flags, error); @@ -1153,6 +1159,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "LockPersonality")) return bus_set_transient_bool(u, name, &c->lock_personality, message, flags, error); + if (streq(name, "ProtectHostname")) + return bus_set_transient_bool(u, name, &c->protect_hostname, message, flags, error); + if (streq(name, "UtmpIdentifier")) return bus_set_transient_string(u, name, &c->utmp_id, message, flags, error); @@ -1213,6 +1222,9 @@ int bus_exec_context_set_transient_property( if (streq(name, "MountFlags")) return bus_set_transient_mount_flags(u, name, &c->mount_flags, message, flags, error); + if (streq(name, "NetworkNamespacePath")) + return bus_set_transient_path(u, name, &c->network_namespace_path, message, flags, error); + if (streq(name, "SupplementaryGroups")) { _cleanup_strv_free_ char **l = NULL; char **p; @@ -1383,7 +1395,7 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; - bool invert = !whitelist; + SeccompParseFlags invert_flag = whitelist ? 0 : SECCOMP_PARSE_INVERT; char **s; if (strv_isempty(l)) { @@ -1402,7 +1414,12 @@ int bus_exec_context_set_transient_property( c->syscall_whitelist = whitelist; if (c->syscall_whitelist) { - r = seccomp_parse_syscall_filter("@default", -1, c->syscall_filter, SECCOMP_PARSE_WHITELIST | (invert ? SECCOMP_PARSE_INVERT : 0)); + r = seccomp_parse_syscall_filter("@default", + -1, + c->syscall_filter, + SECCOMP_PARSE_WHITELIST | invert_flag, + u->id, + NULL, 0); if (r < 0) return r; } @@ -1416,7 +1433,12 @@ int bus_exec_context_set_transient_property( if (r < 0) return r; - r = seccomp_parse_syscall_filter(n, e, c->syscall_filter, (invert ? SECCOMP_PARSE_INVERT : 0) | (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0)); + r = seccomp_parse_syscall_filter(n, + e, + c->syscall_filter, + (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0) | invert_flag, + u->id, + NULL, 0); if (r < 0) return r; } @@ -1494,7 +1516,6 @@ int bus_exec_context_set_transient_property( if (!UNIT_WRITE_FLAGS_NOOP(flags)) { _cleanup_free_ char *joined = NULL; - bool invert = !whitelist; char **s; if (strv_isempty(l)) { @@ -1520,7 +1541,7 @@ int bus_exec_context_set_transient_property( if (af < 0) return af; - if (!invert == c->address_families_whitelist) { + if (whitelist == c->address_families_whitelist) { r = set_put(c->address_families, INT_TO_PTR(af)); if (r < 0) return r; @@ -2177,26 +2198,21 @@ int bus_exec_context_set_transient_property( } if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - char ***dirs = NULL; ExecDirectoryType i; + ExecDirectory *d; - for (i = 0; i < _EXEC_DIRECTORY_TYPE_MAX; i++) - if (streq(name, exec_directory_type_to_string(i))) { - dirs = &c->directories[i].paths; - break; - } - - assert(dirs); + assert_se((i = exec_directory_type_from_string(name)) >= 0); + d = c->directories + i; if (strv_isempty(l)) { - *dirs = strv_free(*dirs); + d->paths = strv_free(d->paths); unit_write_settingf(u, flags, name, "%s=", name); } else { _cleanup_free_ char *joined = NULL; - r = strv_extend_strv(dirs, l, true); + r = strv_extend_strv(&d->paths, l, true); if (r < 0) - return -ENOMEM; + return r; joined = unit_concat_strv(l, UNIT_ESCAPE_SPECIFIERS); if (!joined) diff --git a/src/core/dbus-manager.c b/src/core/dbus-manager.c index 88e4c6bb9..de997c330 100644 --- a/src/core/dbus-manager.c +++ b/src/core/dbus-manager.c @@ -334,8 +334,25 @@ static int bus_load_unit_by_name(Manager *m, sd_bus_message *message, const char return manager_load_unit(m, name, NULL, error, ret_unit); } -static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { +static int reply_unit_path(Unit *u, sd_bus_message *message, sd_bus_error *error) { _cleanup_free_ char *path = NULL; + int r; + + assert(u); + assert(message); + + r = mac_selinux_unit_access_check(u, message, "status", error); + if (r < 0) + return r; + + path = unit_dbus_path(u); + if (!path) + return log_oom(); + + return sd_bus_reply_method_return(message, "o", path); +} + +static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; const char *name; Unit *u; @@ -354,19 +371,10 @@ static int method_get_unit(sd_bus_message *message, void *userdata, sd_bus_error if (r < 0) return r; - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); + return reply_unit_path(u, message, error); } static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; Manager *m = userdata; pid_t pid; Unit *u; @@ -401,15 +409,7 @@ static int method_get_unit_by_pid(sd_bus_message *message, void *userdata, sd_bu if (!u) return sd_bus_error_setf(error, BUS_ERROR_NO_UNIT_FOR_PID, "PID "PID_FMT" does not belong to any loaded unit.", pid); - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); + return reply_unit_path(u, message, error); } static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -471,7 +471,6 @@ static int method_get_unit_by_invocation_id(sd_bus_message *message, void *userd } static int method_get_unit_by_control_group(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; Manager *m = userdata; const char *cgroup; Unit *u; @@ -485,19 +484,10 @@ static int method_get_unit_by_control_group(sd_bus_message *message, void *userd if (!u) return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_UNIT, "Control group '%s' is not valid or not managed by this instance", cgroup); - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); + return reply_unit_path(u, message, error); } static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; Manager *m = userdata; const char *name; Unit *u; @@ -516,15 +506,7 @@ static int method_load_unit(sd_bus_message *message, void *userdata, sd_bus_erro if (r < 0) return r; - r = mac_selinux_unit_access_check(u, message, "status", error); - if (r < 0) - return r; - - path = unit_dbus_path(u); - if (!path) - return -ENOMEM; - - return sd_bus_reply_method_return(message, "o", path); + return reply_unit_path(u, message, error); } static int method_start_unit_generic(sd_bus_message *message, Manager *m, JobType job_type, bool reload_if_possible, sd_bus_error *error) { @@ -574,6 +556,53 @@ static int method_reload_or_try_restart_unit(sd_bus_message *message, void *user return method_start_unit_generic(message, userdata, JOB_TRY_RESTART, true, error); } +typedef enum GenericUnitOperationFlags { + GENERIC_UNIT_LOAD = 1 << 0, /* Load if the unit is not loaded yet */ + GENERIC_UNIT_VALIDATE_LOADED = 1 << 1, /* Verify unit is properly loaded before forwarding call */ +} GenericUnitOperationFlags; + +static int method_generic_unit_operation( + sd_bus_message *message, + Manager *m, + sd_bus_error *error, + sd_bus_message_handler_t handler, + GenericUnitOperationFlags flags) { + + const char *name; + Unit *u; + int r; + + assert(message); + assert(m); + + /* Read the first argument from the command and pass the operation to the specified per-unit + * method. */ + + r = sd_bus_message_read(message, "s", &name); + if (r < 0) + return r; + + if (!isempty(name) && FLAGS_SET(flags, GENERIC_UNIT_LOAD)) + r = manager_load_unit(m, name, NULL, error, &u); + else + r = bus_get_unit_by_name(m, message, name, &u, error); + if (r < 0) + return r; + + if (FLAGS_SET(flags, GENERIC_UNIT_VALIDATE_LOADED)) { + r = bus_unit_validate_load_state(u, error); + if (r < 0) + return r; + } + + return handler(message, u, error); +} + +static int method_enqueue_unit_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { + /* We don't bother with GENERIC_UNIT_VALIDATE_LOADED here, as the job logic validates that anyway */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_enqueue_job, GENERIC_UNIT_LOAD); +} + static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd_bus_error *error) { Manager *m = userdata; const char *old_name; @@ -597,115 +626,31 @@ static int method_start_unit_replace(sd_bus_message *message, void *userdata, sd } static int method_kill_unit(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; - - return bus_unit_method_kill(message, u, error); + /* We don't bother with GENERIC_UNIT_LOAD nor GENERIC_UNIT_VALIDATE_LOADED here, as it shouldn't + * matter whether a unit is loaded for killing any processes possibly in the unit's cgroup. */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_kill, 0); } static int method_reset_failed_unit(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; - - return bus_unit_method_reset_failed(message, u, error); + /* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the + * unit to be loaded properly (since a failed unit might have its unit file disappeared) */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_reset_failed, 0); } static int method_set_unit_properties(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_load_unit_by_name(m, message, name, &u, error); - if (r < 0) - return r; - - r = bus_unit_validate_load_state(u, error); - if (r < 0) - return r; - - return bus_unit_method_set_properties(message, u, error); + /* Only change properties on fully loaded units, and load them in order to set properties */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_set_properties, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED); } static int method_ref_unit(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_load_unit_by_name(m, message, name, &u, error); - if (r < 0) - return r; - - r = bus_unit_validate_load_state(u, error); - if (r < 0) - return r; - - return bus_unit_method_ref(message, u, error); + /* Only allow reffing of fully loaded units, and make sure reffing a unit loads it. */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_ref, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED); } static int method_unref_unit(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_load_unit_by_name(m, message, name, &u, error); - if (r < 0) - return r; - - r = bus_unit_validate_load_state(u, error); - if (r < 0) - return r; - - return bus_unit_method_unref(message, u, error); + /* Dropping a ref OTOH should not require the unit to still be loaded. And since a reffed unit is a + * loaded unit there's no need to load the unit for unreffing it. */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_unref, 0); } static int reply_unit_info(sd_bus_message *reply, Unit *u) { @@ -783,43 +728,16 @@ static int method_list_units_by_names(sd_bus_message *message, void *userdata, s } static int method_get_unit_processes(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; - - return bus_unit_method_get_processes(message, u, error); + /* Don't load a unit (since it won't have any processes if it's not loaded), but don't insist on the + * unit being loaded (because even improperly loaded units might still have processes around */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_get_processes, 0); } static int method_attach_processes_to_unit(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; - - return bus_unit_method_attach_processes(message, u, error); + /* Don't allow attaching new processes to units that aren't loaded. Don't bother with loading a unit + * for this purpose though, as an unloaded unit is a stopped unit, and we don't allow attaching + * processes to stopped units anyway. */ + return method_generic_unit_operation(message, userdata, error, bus_unit_method_attach_processes, GENERIC_UNIT_VALIDATE_LOADED); } static int transient_unit_from_message( @@ -955,7 +873,7 @@ static int method_start_transient_unit(sd_bus_message *message, void *userdata, return r; /* Finally, start it */ - return bus_unit_queue_job(message, u, JOB_START, mode, false, error); + return bus_unit_queue_job(message, u, JOB_START, mode, 0, error); } static int method_get_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -1490,7 +1408,7 @@ static int method_kexec(sd_bus_message *message, void *userdata, sd_bus_error *e } static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_error *error) { - char *ri = NULL, *rt = NULL; + _cleanup_free_ char *ri = NULL, *rt = NULL; const char *root, *init; Manager *m = userdata; struct statvfs svfs; @@ -1562,17 +1480,12 @@ static int method_switch_root(sd_bus_message *message, void *userdata, sd_bus_er if (!isempty(init)) { ri = strdup(init); - if (!ri) { - free(rt); + if (!ri) return -ENOMEM; - } } - free(m->switch_root); - m->switch_root = rt; - - free(m->switch_root_init); - m->switch_root_init = ri; + free_and_replace(m->switch_root, rt); + free_and_replace(m->switch_root_init, ri); m->objective = MANAGER_SWITCH_ROOT; @@ -2553,6 +2466,7 @@ const sd_bus_vtable bus_manager_vtable[] = { SD_BUS_METHOD("TryRestartUnit", "ss", "o", method_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReloadOrRestartUnit", "ss", "o", method_reload_or_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReloadOrTryRestartUnit", "ss", "o", method_reload_or_try_restart_unit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("EnqueueUnitJob", "sss", "uososa(uosos)", method_enqueue_unit_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("KillUnit", "ssi", NULL, method_kill_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResetFailedUnit", "s", NULL, method_reset_failed_unit, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetUnitProperties", "sba(sv)", NULL, method_set_unit_properties, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/core/dbus-scope.c b/src/core/dbus-scope.c index bb807df2e..8eb915e50 100644 --- a/src/core/dbus-scope.c +++ b/src/core/dbus-scope.c @@ -106,7 +106,7 @@ static int bus_scope_set_transient_property( return r; if (!UNIT_WRITE_FLAGS_NOOP(flags)) { - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, false); if (r < 0 && r != -EEXIST) return r; } diff --git a/src/core/dbus-service.c b/src/core/dbus-service.c index 0904cc09c..48c0fb74a 100644 --- a/src/core/dbus-service.c +++ b/src/core/dbus-service.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "async.h" @@ -55,7 +56,7 @@ static int property_get_exit_status_set( return r; SET_FOREACH(id, status_set->status, i) { - int val = PTR_TO_INT(id); + int32_t val = PTR_TO_INT(id); if (val < 0 || val > 255) continue; @@ -74,10 +75,10 @@ static int property_get_exit_status_set( return r; SET_FOREACH(id, status_set->signal, i) { - int val = PTR_TO_INT(id); + int32_t val = PTR_TO_INT(id); const char *str; - str = signal_to_string(val); + str = signal_to_string((int) val); if (!str) continue; @@ -120,6 +121,7 @@ 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("ReloadResult", "s", property_get_result, offsetof(Service, reload_result), 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), @@ -151,7 +153,7 @@ static int bus_set_transient_exit_status( UnitWriteFlags flags, sd_bus_error *error) { - const int *status, *signal; + const int32_t *status, *signal; size_t sz_status, sz_signal, i; int r; @@ -171,6 +173,9 @@ static int bus_set_transient_exit_status( if (r < 0) return r; + sz_status /= sizeof(int32_t); + sz_signal /= sizeof(int32_t); + if (sz_status == 0 && sz_signal == 0 && !UNIT_WRITE_FLAGS_NOOP(flags)) { exit_status_set_free(status_set); unit_write_settingf(u, flags, name, "%s=", name); @@ -179,34 +184,34 @@ static int bus_set_transient_exit_status( for (i = 0; i < sz_status; i++) { if (status[i] < 0 || status[i] > 255) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid status code in %s: %i", name, status[i]); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid status code in %s: %"PRIi32, name, status[i]); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { r = set_ensure_allocated(&status_set->status, NULL); if (r < 0) return r; - r = set_put(status_set->status, INT_TO_PTR(status[i])); + r = set_put(status_set->status, INT_TO_PTR((int) status[i])); if (r < 0) return r; - unit_write_settingf(u, flags, name, "%s=%i", name, status[i]); + unit_write_settingf(u, flags, name, "%s=%"PRIi32, name, status[i]); } } for (i = 0; i < sz_signal; i++) { const char *str; - str = signal_to_string(signal[i]); + str = signal_to_string((int) signal[i]); if (!str) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal in %s: %i", name, signal[i]); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid signal in %s: %"PRIi32, name, signal[i]); if (!UNIT_WRITE_FLAGS_NOOP(flags)) { r = set_ensure_allocated(&status_set->signal, NULL); if (r < 0) return r; - r = set_put(status_set->signal, INT_TO_PTR(signal[i])); + r = set_put(status_set->signal, INT_TO_PTR((int) signal[i])); if (r < 0) return r; diff --git a/src/core/dbus-timer.c b/src/core/dbus-timer.c index b9d2f3d07..807ca8022 100644 --- a/src/core/dbus-timer.c +++ b/src/core/dbus-timer.c @@ -123,6 +123,8 @@ const sd_bus_vtable bus_timer_vtable[] = { SD_BUS_PROPERTY("Unit", "s", bus_property_get_triggered_unit, 0, SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("TimersMonotonic", "a(stt)", property_get_monotonic_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("TimersCalendar", "a(sst)", property_get_calendar_timers, 0, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), + SD_BUS_PROPERTY("OnClockChange", "b", bus_property_get_bool, offsetof(Timer, on_clock_change), SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_PROPERTY("OnTimezoneChange", "b", bus_property_get_bool, offsetof(Timer, on_timezone_change), SD_BUS_VTABLE_PROPERTY_CONST), SD_BUS_PROPERTY("NextElapseUSecRealtime", "t", bus_property_get_usec, offsetof(Timer, next_elapse_realtime), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("NextElapseUSecMonotonic", "t", property_get_next_elapse_monotonic, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), BUS_PROPERTY_DUAL_TIMESTAMP("LastTriggerUSec", offsetof(Timer, last_trigger), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), @@ -171,6 +173,12 @@ static int bus_timer_set_transient_property( if (streq(name, "RemainAfterElapse")) return bus_set_transient_bool(u, name, &t->remain_after_elapse, message, flags, error); + if (streq(name, "OnTimezoneChange")) + return bus_set_transient_bool(u, name, &t->on_timezone_change, message, flags, error); + + if (streq(name, "OnClockChange")) + return bus_set_transient_bool(u, name, &t->on_clock_change, message, flags, error); + if (streq(name, "TimersMonotonic")) { const char *base_name; usec_t usec = 0; @@ -194,12 +202,14 @@ static int bus_timer_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, format_timespan(ts, sizeof(ts), usec, USEC_PER_MSEC)); - v = new0(TimerValue, 1); + v = new(TimerValue, 1); if (!v) return -ENOMEM; - v->base = b; - v->value = usec; + *v = (TimerValue) { + .base = b, + .value = usec, + }; LIST_PREPEND(value, t->values, v); } @@ -247,12 +257,14 @@ static int bus_timer_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", base_name, str); - v = new0(TimerValue, 1); + v = new(TimerValue, 1); if (!v) return -ENOMEM; - v->base = b; - v->calendar_spec = TAKE_PTR(c); + *v = (TimerValue) { + .base = b, + .calendar_spec = TAKE_PTR(c), + }; LIST_PREPEND(value, t->values, v); } @@ -300,12 +312,14 @@ static int bus_timer_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, format_timespan(time, sizeof(time), usec, USEC_PER_MSEC)); - v = new0(TimerValue, 1); + v = new(TimerValue, 1); if (!v) return -ENOMEM; - v->base = b; - v->value = usec; + *v = (TimerValue) { + .base = b, + .value = usec, + }; LIST_PREPEND(value, t->values, v); } @@ -333,12 +347,14 @@ static int bus_timer_set_transient_property( unit_write_settingf(u, flags|UNIT_ESCAPE_SPECIFIERS, name, "%s=%s", name, str); - v = new0(TimerValue, 1); + v = new(TimerValue, 1); if (!v) return -ENOMEM; - v->base = TIMER_CALENDAR; - v->calendar_spec = TAKE_PTR(c); + *v = (TimerValue) { + .base = TIMER_CALENDAR, + .calendar_spec = TAKE_PTR(c), + }; LIST_PREPEND(value, t->values, v); } diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index 17c2003c8..6f1a74d6b 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -312,6 +312,14 @@ static int bus_verify_manage_units_async_full( error); } +static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { + [JOB_START] = N_("Authentication is required to start '$(unit)'."), + [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), + [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), + [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), + [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), +}; + int bus_unit_method_start_generic( sd_bus_message *message, Unit *u, @@ -319,16 +327,8 @@ int bus_unit_method_start_generic( bool reload_if_possible, sd_bus_error *error) { - const char *smode; + const char *smode, *verb; JobMode mode; - _cleanup_free_ char *verb = NULL; - static const char *const polkit_message_for_job[_JOB_TYPE_MAX] = { - [JOB_START] = N_("Authentication is required to start '$(unit)'."), - [JOB_STOP] = N_("Authentication is required to stop '$(unit)'."), - [JOB_RELOAD] = N_("Authentication is required to reload '$(unit)'."), - [JOB_RESTART] = N_("Authentication is required to restart '$(unit)'."), - [JOB_TRY_RESTART] = N_("Authentication is required to restart '$(unit)'."), - }; int r; assert(message); @@ -351,17 +351,15 @@ int bus_unit_method_start_generic( return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); if (reload_if_possible) - verb = strjoin("reload-or-", job_type_to_string(job_type)); + verb = strjoina("reload-or-", job_type_to_string(job_type)); else - verb = strdup(job_type_to_string(job_type)); - if (!verb) - return -ENOMEM; + verb = job_type_to_string(job_type); r = bus_verify_manage_units_async_full( u, verb, CAP_SYS_ADMIN, - job_type < _JOB_TYPE_MAX ? polkit_message_for_job[job_type] : NULL, + polkit_message_for_job[job_type], true, message, error); @@ -370,7 +368,8 @@ int bus_unit_method_start_generic( if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - return bus_unit_queue_job(message, u, job_type, mode, reload_if_possible, error); + return bus_unit_queue_job(message, u, job_type, mode, + reload_if_possible ? BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE : 0, error); } static int method_start(sd_bus_message *message, void *userdata, sd_bus_error *error) { @@ -401,6 +400,62 @@ static int method_reload_or_try_restart(sd_bus_message *message, void *userdata, return bus_unit_method_start_generic(message, userdata, JOB_TRY_RESTART, true, error); } +int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_error *error) { + BusUnitQueueFlags flags = BUS_UNIT_QUEUE_VERBOSE_REPLY; + const char *jtype, *smode; + Unit *u = userdata; + JobType type; + JobMode mode; + int r; + + assert(message); + assert(u); + + r = sd_bus_message_read(message, "ss", &jtype, &smode); + if (r < 0) + return r; + + /* Parse the two magic reload types "reload-or-…" manually */ + if (streq(jtype, "reload-or-restart")) { + type = JOB_RESTART; + flags |= BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE; + } else if (streq(jtype, "reload-or-try-restart")) { + type = JOB_TRY_RESTART; + flags |= BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE; + } else { + /* And the rest generically */ + type = job_type_from_string(jtype); + if (type < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job type %s invalid", jtype); + } + + mode = job_mode_from_string(smode); + if (mode < 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Job mode %s invalid", smode); + + r = mac_selinux_unit_access_check( + u, message, + job_type_to_access_method(type), + error); + if (r < 0) + return r; + + r = bus_verify_manage_units_async_full( + u, + jtype, + CAP_SYS_ADMIN, + polkit_message_for_job[type], + true, + message, + error); + if (r < 0) + return r; + if (r == 0) + return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ + + return bus_unit_queue_job(message, u, type, mode, flags, error); +} + int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error) { Unit *u = userdata; const char *swho; @@ -686,6 +741,7 @@ const sd_bus_vtable bus_unit_vtable[] = { SD_BUS_METHOD("TryRestart", "s", "o", method_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReloadOrRestart", "s", "o", method_reload_or_restart, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ReloadOrTryRestart", "s", "o", method_reload_or_try_restart, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("EnqueueJob", "ss", "uososa(uosos)", bus_unit_method_enqueue_job, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Kill", "si", NULL, bus_unit_method_kill, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("ResetFailed", NULL, NULL, bus_unit_method_reset_failed, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetProperties", "ba(sv)", NULL, bus_unit_method_set_properties, SD_BUS_VTABLE_UNPRIVILEGED), @@ -1272,11 +1328,14 @@ int bus_unit_queue_job( Unit *u, JobType type, JobMode mode, - bool reload_if_possible, + BusUnitQueueFlags flags, sd_bus_error *error) { - _cleanup_free_ char *path = NULL; - Job *j; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + _cleanup_free_ char *job_path = NULL, *unit_path = NULL; + _cleanup_(set_freep) Set *affected = NULL; + Iterator i; + Job *j, *a; int r; assert(message); @@ -1291,7 +1350,7 @@ int bus_unit_queue_job( if (r < 0) return r; - if (reload_if_possible && unit_can_reload(u)) { + if (FLAGS_SET(flags, BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE) && unit_can_reload(u)) { if (type == JOB_RESTART) type = JOB_RELOAD_OR_START; else if (type == JOB_TRY_RESTART) @@ -1309,7 +1368,13 @@ int bus_unit_queue_job( (type == JOB_RELOAD_OR_START && job_type_collapse(type, u) == JOB_START && u->refuse_manual_start)) return sd_bus_error_setf(error, BUS_ERROR_ONLY_BY_DEPENDENCY, "Operation refused, unit %s may be requested by dependency only (it is configured to refuse manual start/stop).", u->id); - r = manager_add_job(u->manager, type, u, mode, error, &j); + if (FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) { + affected = set_new(NULL); + if (!affected) + return -ENOMEM; + } + + r = manager_add_job(u->manager, type, u, mode, affected, error, &j); if (r < 0) return r; @@ -1317,14 +1382,67 @@ int bus_unit_queue_job( if (r < 0) return r; - path = job_dbus_path(j); - 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); + job_path = job_dbus_path(j); + if (!job_path) + return -ENOMEM; + + /* The classic response is just a job object path */ + if (!FLAGS_SET(flags, BUS_UNIT_QUEUE_VERBOSE_REPLY)) + return sd_bus_reply_method_return(message, "o", job_path); + + /* In verbose mode respond with the anchor job plus everything that has been affected */ + r = sd_bus_message_new_method_return(message, &reply); + if (r < 0) + return r; + + unit_path = unit_dbus_path(j->unit); + if (!unit_path) + return -ENOMEM; + + r = sd_bus_message_append(reply, "uosos", + j->id, job_path, + j->unit->id, unit_path, + job_type_to_string(j->type)); + if (r < 0) + return r; + + r = sd_bus_message_open_container(reply, 'a', "(uosos)"); + if (r < 0) + return r; + + SET_FOREACH(a, affected, i) { + + if (a->id == j->id) + continue; + + /* Free paths from previous iteration */ + job_path = mfree(job_path); + unit_path = mfree(unit_path); + + job_path = job_dbus_path(a); + if (!job_path) + return -ENOMEM; + + unit_path = unit_dbus_path(a->unit); + if (!unit_path) + return -ENOMEM; + + r = sd_bus_message_append(reply, "(uosos)", + a->id, job_path, + a->unit->id, unit_path, + job_type_to_string(a->type)); + if (r < 0) + return r; + } + + r = sd_bus_message_close_container(reply); + if (r < 0) + return r; + + return sd_bus_send(NULL, reply, NULL); } static int bus_unit_set_live_property( @@ -1674,9 +1792,17 @@ static int bus_unit_set_transient_property( return r; STRV_FOREACH(p, l) { + path_simplify(*p, true); + if (!path_is_absolute(*p)) return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s is not absolute: %s", name, *p); + if (!path_is_valid(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s has invalid length: %s", name, *p); + + if (!path_is_normalized(*p)) + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Path specified in %s is not normalized: %s", name, *p); + if (!UNIT_WRITE_FLAGS_NOOP(flags)) { r = unit_require_mounts_for(u, *p, UNIT_DEPENDENCY_FILE); if (r < 0) diff --git a/src/core/dbus-unit.h b/src/core/dbus-unit.h index 345345e3e..740bb1c86 100644 --- a/src/core/dbus-unit.h +++ b/src/core/dbus-unit.h @@ -15,6 +15,7 @@ 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); +int bus_unit_method_enqueue_job(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_method_kill(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_method_reset_failed(sd_bus_message *message, void *userdata, sd_bus_error *error); @@ -25,7 +26,12 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error); int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error); -int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, bool reload_if_possible, sd_bus_error *error); +typedef enum BusUnitQueueFlags { + BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0, + BUS_UNIT_QUEUE_VERBOSE_REPLY = 1 << 1, +} BusUnitQueueFlags; + +int bus_unit_queue_job(sd_bus_message *message, Unit *u, JobType type, JobMode mode, BusUnitQueueFlags flags, sd_bus_error *error); int bus_unit_validate_load_state(Unit *u, sd_bus_error *error); int bus_unit_track_add_name(Unit *u, const char *name); diff --git a/src/core/dbus.c b/src/core/dbus.c index 255b86e7a..8ae5d173f 100644 --- a/src/core/dbus.c +++ b/src/core/dbus.c @@ -177,7 +177,7 @@ static int signal_activation_request(sd_bus_message *message, void *userdata, sd goto failed; } - r = manager_add_job(m, JOB_START, u, JOB_REPLACE, &error, NULL); + r = manager_add_job(m, JOB_START, u, JOB_REPLACE, NULL, &error, NULL); if (r < 0) goto failed; @@ -622,6 +622,9 @@ static int bus_on_connection(sd_event_source *s, int fd, uint32_t revents, void nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (nfd < 0) { + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + return 0; + log_warning_errno(errno, "Failed to accept private connection, ignoring: %m"); return 0; } diff --git a/src/core/device.c b/src/core/device.c index b006add40..9f7caa49e 100644 --- a/src/core/device.c +++ b/src/core/device.c @@ -17,6 +17,7 @@ #include "stat-util.h" #include "string-util.h" #include "swap.h" +#include "udev-util.h" #include "unit-name.h" #include "unit.h" @@ -432,7 +433,7 @@ static int device_add_udev_wants(Unit *u, sd_device *dev) { if (strv_contains(d->wants_property, *i)) /* Was this unit already listed before? */ continue; - r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, &error, NULL); + r = manager_add_job_by_name(u->manager, JOB_START, *i, JOB_FAIL, NULL, &error, NULL); if (r < 0) log_unit_warning_errno(u, r, "Failed to enqueue SYSTEMD_WANTS= job, ignoring: %s", bus_error_message(&error, r)); } @@ -665,9 +666,13 @@ static void device_found_changed(Device *d, DeviceFound previous, DeviceFound no } static void device_update_found_one(Device *d, DeviceFound found, DeviceFound mask) { + Manager *m; + assert(d); - if (MANAGER_IS_RUNNING(UNIT(d)->manager)) { + m = UNIT(d)->manager; + + if (MANAGER_IS_RUNNING(m) && (m->honor_device_enumeration || MANAGER_IS_USER(m))) { DeviceFound n, previous; /* When we are already running, then apply the new mask right-away, and trigger state changes @@ -729,6 +734,9 @@ static bool device_is_ready(sd_device *dev) { assert(dev); + if (device_is_renaming(dev) > 0) + return false; + if (sd_device_get_property_value(dev, "SYSTEMD_READY", &ready) < 0) return true; @@ -889,7 +897,8 @@ static void device_propagate_reload_by_sysfs(Manager *m, const char *sysfs) { static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void *userdata) { Manager *m = userdata; - const char *action, *sysfs; + DeviceAction action; + const char *sysfs; int r; assert(m); @@ -901,19 +910,19 @@ static int device_dispatch_io(sd_device_monitor *monitor, sd_device *dev, void * return 0; } - r = sd_device_get_property_value(dev, "ACTION", &action); + r = device_get_action(dev, &action); if (r < 0) { - log_device_error_errno(dev, r, "Failed to get udev action string: %m"); + log_device_error_errno(dev, r, "Failed to get udev action: %m"); return 0; } - if (streq(action, "change")) + if (action == DEVICE_ACTION_CHANGE) device_propagate_reload_by_sysfs(m, sysfs); /* A change event can signal that a device is becoming ready, in particular if * the device is using the SYSTEMD_READY logic in udev * so we need to reach the else block of the follwing if, even for change events */ - if (streq(action, "remove")) { + if (action == DEVICE_ACTION_REMOVE) { r = swap_process_device_remove(m, dev); if (r < 0) log_device_warning_errno(dev, r, "Failed to process swap device remove event, ignoring: %m"); diff --git a/src/core/dynamic-user.c b/src/core/dynamic-user.c index 530df70b8..c380bee9e 100644 --- a/src/core/dynamic-user.c +++ b/src/core/dynamic-user.c @@ -3,11 +3,14 @@ #include #include #include +#include +#include #include "clean-ipc.h" #include "dynamic-user.h" #include "fd-util.h" #include "fileio.h" +#include "format-util.h" #include "fs-util.h" #include "io-util.h" #include "nscd-flush.h" @@ -707,7 +710,7 @@ int dynamic_user_lookup_uid(Manager *m, uid_t uid, char **ret) { xsprintf(lock_path, "/run/systemd/dynamic-uid/" UID_FMT, uid); r = read_one_line_file(lock_path, &user); - if (r == -ENOENT) + if (IN_SET(r, -ENOENT, 0)) return -ESRCH; if (r < 0) return r; diff --git a/src/core/dynamic-user.h b/src/core/dynamic-user.h index 112f91e63..0a55630ab 100644 --- a/src/core/dynamic-user.h +++ b/src/core/dynamic-user.h @@ -15,8 +15,8 @@ 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 { - unsigned n_ref; Manager *manager; + unsigned n_ref; /* An AF_UNIX socket pair that contains a datagram containing both the numeric ID assigned, as well as a lock * file fd locking the user ID we picked. */ diff --git a/src/core/emergency-action.c b/src/core/emergency-action.c index f98b0de79..4330c0f2c 100644 --- a/src/core/emergency-action.c +++ b/src/core/emergency-action.c @@ -20,7 +20,7 @@ static void log_and_status(Manager *m, bool warn, const char *message, const cha "%s: %s", message, reason); } -int emergency_action( +void emergency_action( Manager *m, EmergencyAction action, EmergencyActionFlags options, @@ -33,11 +33,11 @@ int emergency_action( assert(action < _EMERGENCY_ACTION_MAX); if (action == EMERGENCY_ACTION_NONE) - return -ECANCELED; + return; if (FLAGS_SET(options, EMERGENCY_ACTION_IS_WATCHDOG) && !m->service_watchdogs) { log_warning("Watchdog disabled! Not acting on: %s", reason); - return -ECANCELED; + return; } bool warn = FLAGS_SET(options, EMERGENCY_ACTION_WARN); @@ -47,15 +47,14 @@ int emergency_action( case EMERGENCY_ACTION_REBOOT: 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); - + (void) update_reboot_parameter_and_warn(reboot_arg, true); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_REBOOT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL); break; case EMERGENCY_ACTION_REBOOT_FORCE: log_and_status(m, warn, "Forcibly rebooting", reason); - (void) update_reboot_parameter_and_warn(reboot_arg); + (void) update_reboot_parameter_and_warn(reboot_arg, true); m->objective = MANAGER_REBOOT; break; @@ -82,7 +81,7 @@ int emergency_action( 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); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL); break; } @@ -91,7 +90,7 @@ int emergency_action( case EMERGENCY_ACTION_POWEROFF: 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); + (void) manager_add_job_by_name_and_warn(m, JOB_START, SPECIAL_POWEROFF_TARGET, JOB_REPLACE_IRREVERSIBLY, NULL, NULL); break; case EMERGENCY_ACTION_EXIT_FORCE: @@ -125,8 +124,6 @@ int emergency_action( default: assert_not_reached("Unknown emergency action"); } - - return -ECANCELED; } static const char* const emergency_action_table[_EMERGENCY_ACTION_MAX] = { diff --git a/src/core/emergency-action.h b/src/core/emergency-action.h index 6e6c69ddf..706c38a7d 100644 --- a/src/core/emergency-action.h +++ b/src/core/emergency-action.h @@ -24,9 +24,9 @@ typedef enum EmergencyActionFlags { #include "macro.h" #include "manager.h" -int emergency_action(Manager *m, - EmergencyAction action, EmergencyActionFlags options, - const char *reboot_arg, int exit_status, const char *reason); +void 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_; diff --git a/src/core/execute.c b/src/core/execute.c index a7082310b..5e1a74d0b 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -65,6 +65,7 @@ #include "log.h" #include "macro.h" #include "manager.h" +#include "memory-util.h" #include "missing.h" #include "mkdir.h" #include "namespace.h" @@ -91,7 +92,6 @@ #include "umask-util.h" #include "unit.h" #include "user-util.h" -#include "util.h" #include "utmp-wtmp.h" #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) @@ -1404,13 +1404,15 @@ static bool context_has_no_new_privileges(const ExecContext *c) { return context_has_address_families(c) || c->memory_deny_write_execute || c->restrict_realtime || + c->restrict_suid_sgid || exec_context_restrict_namespaces_set(c) || c->protect_kernel_tunables || c->protect_kernel_modules || c->private_devices || context_has_syscall_filters(c) || !set_isempty(c->syscall_archs) || - c->lock_personality; + c->lock_personality || + c->protect_hostname; } #if HAVE_SECCOMP @@ -1508,6 +1510,19 @@ static int apply_restrict_realtime(const Unit* u, const ExecContext *c) { return seccomp_restrict_realtime(); } +static int apply_restrict_suid_sgid(const Unit* u, const ExecContext *c) { + assert(u); + assert(c); + + if (!c->restrict_suid_sgid) + return 0; + + if (skip_seccomp_unavailable(u, "RestrictSUIDSGID=")) + return 0; + + return seccomp_restrict_suid_sgid(); +} + static int apply_protect_sysctl(const Unit *u, const ExecContext *c) { assert(u); assert(c); @@ -1696,6 +1711,8 @@ static int build_environment( x = strappend("HOME=", home); if (!x) return -ENOMEM; + + path_simplify(x + 5, true); our_env[n_env++] = x; } @@ -1715,6 +1732,8 @@ static int build_environment( x = strappend("SHELL=", shell); if (!x) return -ENOMEM; + + path_simplify(x + 6, true); our_env[n_env++] = x; } @@ -1839,7 +1858,7 @@ static bool exec_needs_mount_namespace( if (context->n_temporary_filesystems > 0) return true; - if (context->mount_flags != 0) + if (!IN_SET(context->mount_flags, 0, MS_SHARED)) return true; if (context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) @@ -2069,7 +2088,7 @@ static int setup_exec_directory( STRV_FOREACH(rt, context->directories[type].paths) { _cleanup_free_ char *p = NULL, *pp = NULL; - p = strjoin(params->prefix[type], "/", *rt); + p = path_join(params->prefix[type], *rt); if (!p) { r = -ENOMEM; goto fail; @@ -2080,7 +2099,8 @@ static int setup_exec_directory( goto fail; if (context->dynamic_user && - !IN_SET(type, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION)) { + (!IN_SET(type, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION) || + (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode != EXEC_PRESERVE_NO))) { _cleanup_free_ char *private_root = NULL; /* So, here's one extra complication when dealing with DynamicUser=1 units. In that case we @@ -2105,7 +2125,7 @@ static int setup_exec_directory( * Also, note that we don't do this for EXEC_DIRECTORY_RUNTIME as that's often used for sharing * files or sockets with other services. */ - private_root = strjoin(params->prefix[type], "/private"); + private_root = path_join(params->prefix[type], "private"); if (!private_root) { r = -ENOMEM; goto fail; @@ -2116,7 +2136,7 @@ static int setup_exec_directory( if (r < 0) goto fail; - pp = strjoin(private_root, "/", *rt); + pp = path_join(private_root, *rt); if (!pp) { r = -ENOMEM; goto fail; @@ -2151,39 +2171,47 @@ static int setup_exec_directory( if (r < 0) goto fail; - /* Lock down the access mode */ - if (chmod(pp, context->directories[type].mode) < 0) { - r = -errno; - goto fail; - } } else { r = mkdir_label(p, context->directories[type].mode); - if (r < 0 && r != -EEXIST) - goto fail; - if (r == -EEXIST) { - struct stat st; - - if (stat(p, &st) < 0) { - r = -errno; + if (r < 0) { + if (r != -EEXIST) goto fail; - } - if (((st.st_mode ^ context->directories[type].mode) & 07777) != 0) - log_warning("%s \'%s\' already exists but the mode is different. " - "(filesystem: %o %sMode: %o)", - exec_directory_type_to_string(type), *rt, - st.st_mode & 07777, exec_directory_type_to_string(type), context->directories[type].mode & 07777); - if (!context->dynamic_user) + + if (type == EXEC_DIRECTORY_CONFIGURATION) { + struct stat st; + + /* Don't change the owner/access mode of the configuration directory, + * as in the common case it is not written to by a service, and shall + * not be writable. */ + + if (stat(p, &st) < 0) { + r = -errno; + goto fail; + } + + /* Still complain if the access mode doesn't match */ + if (((st.st_mode ^ context->directories[type].mode) & 07777) != 0) + log_warning("%s \'%s\' already exists but the mode is different. " + "(File system: %o %sMode: %o)", + exec_directory_type_to_string(type), *rt, + st.st_mode & 07777, exec_directory_type_to_string(type), context->directories[type].mode & 07777); + continue; + } } } - /* Don't change the owner of the configuration directory, as in the common case it is not written to by - * a service, and shall not be writable. */ - if (type == EXEC_DIRECTORY_CONFIGURATION) - continue; + /* Lock down the access mode (we use chmod_and_chown() to make this idempotent. We don't + * specifiy UID/GID here, so that path_chown_recursive() can optimize things depending on the + * current UID/GID ownership.) */ + r = chmod_and_chown(pp ?: p, context->directories[type].mode, UID_INVALID, GID_INVALID); + if (r < 0) + goto fail; - /* Then, change the ownership of the whole tree, if necessary */ - r = path_chown_recursive(pp ?: p, uid, gid); + /* Then, change the ownership of the whole tree, if necessary. When dynamic users are used we + * drop the suid/sgid bits, since we really don't want SUID/SGID files for dynamic UID/GID + * assignments to exist.*/ + r = path_chown_recursive(pp ?: p, uid, gid, context->dynamic_user ? 01777 : 07777); if (r < 0) goto fail; } @@ -2353,6 +2381,7 @@ static int compile_bind_mounts( .source = s, .destination = d, .read_only = false, + .nosuid = context->dynamic_user, /* don't allow suid/sgid when DynamicUser= is on */ .recursive = true, .ignore_enoent = false, }; @@ -2420,6 +2449,7 @@ static int apply_mount_namespace( .protect_control_groups = context->protect_control_groups, .protect_kernel_tunables = context->protect_kernel_tunables, .protect_kernel_modules = context->protect_kernel_modules, + .protect_hostname = context->protect_hostname, .mount_apivfs = context->mount_apivfs, .private_mounts = context->private_mounts, }; @@ -2435,6 +2465,9 @@ static int apply_mount_namespace( else ns_info = (NamespaceInfo) {}; + if (context->mount_flags == MS_SHARED) + log_unit_debug(u, "shared mount propagation hidden by other fs namespacing unit settings: ignoring"); + r = setup_namespace(root_dir, root_image, &ns_info, context->read_write_paths, needs_sandboxing ? context->read_only_paths : NULL, @@ -2733,12 +2766,6 @@ static int acquire_home(const ExecContext *c, uid_t uid, const char** home, char if (!c->working_directory_home) return 0; - if (uid == 0) { - /* Hardcode /root as home directory for UID 0 */ - *home = "/root"; - return 1; - } - r = get_home_dir(buf); if (r < 0) return r; @@ -2841,12 +2868,13 @@ static int exec_child( int user_lookup_fd, int *exit_status) { - _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **final_argv = NULL; + _cleanup_strv_free_ char **our_env = NULL, **pass_env = NULL, **accum_env = NULL, **replaced_argv = 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; + char **final_argv = NULL; dev_t journal_stream_dev = 0; ino_t journal_stream_ino = 0; bool needs_sandboxing, /* Do we need to set up full sandboxing? (i.e. all namespacing, all MAC stuff, caps, yadda yadda */ @@ -3056,6 +3084,14 @@ static int exec_child( } } + if (context->network_namespace_path && runtime && runtime->netns_storage_socket[0] >= 0) { + r = open_netns_path(runtime->netns_storage_socket, context->network_namespace_path); + if (r < 0) { + *exit_status = EXIT_NETWORK; + return log_unit_error_errno(unit, r, "Failed to open network namespace path %s: %m", context->network_namespace_path); + } + } + r = setup_input(context, params, socket_fd, named_iofds); if (r < 0) { *exit_status = EXIT_STDIN; @@ -3142,7 +3178,7 @@ static int exec_child( USER_PROCESS, username); - if (context->user) { + if (uid_is_valid(uid)) { r = chown_terminal(STDIN_FILENO, uid); if (r < 0) { *exit_status = EXIT_STDIN; @@ -3266,13 +3302,17 @@ static int exec_child( } } - if (context->private_network && runtime && runtime->netns_storage_socket[0] >= 0) { + if ((context->private_network || context->network_namespace_path) && runtime && runtime->netns_storage_socket[0] >= 0) { + if (ns_type_supported(NAMESPACE_NET)) { r = setup_netns(runtime->netns_storage_socket); if (r < 0) { *exit_status = EXIT_NETWORK; return log_unit_error_errno(unit, r, "Failed to set up network namespacing: %m"); } + } else if (context->network_namespace_path) { + *exit_status = EXIT_NETWORK; + return log_unit_error_errno(unit, SYNTHETIC_ERRNO(EOPNOTSUPP), "NetworkNamespacePath= is not supported, refusing."); } else log_unit_warning(unit, "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring."); } @@ -3286,6 +3326,23 @@ static int exec_child( } } + if (context->protect_hostname) { + if (ns_type_supported(NAMESPACE_UTS)) { + if (unshare(CLONE_NEWUTS) < 0) { + *exit_status = EXIT_NAMESPACE; + return log_unit_error_errno(unit, errno, "Failed to set up UTS namespacing: %m"); + } + } else + log_unit_warning(unit, "ProtectHostname=yes is configured, but the kernel does not support UTS namespaces, ignoring namespace setup."); +#if HAVE_SECCOMP + r = seccomp_protect_hostname(); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return log_unit_error_errno(unit, r, "Failed to apply hostname restrictions: %m"); + } +#endif + } + /* Drop groups as early as possbile */ if (needs_setuid) { r = enforce_groups(gid, supplementary_gids, ngids); @@ -3426,7 +3483,7 @@ static int exec_child( } if (needs_setuid) { - if (context->user) { + if (uid_is_valid(uid)) { r = enforce_user(context, uid); if (r < 0) { *exit_status = EXIT_USER; @@ -3524,6 +3581,12 @@ static int exec_child( return log_unit_error_errno(unit, r, "Failed to apply realtime restrictions: %m"); } + r = apply_restrict_suid_sgid(unit, context); + if (r < 0) { + *exit_status = EXIT_SECCOMP; + return log_unit_error_errno(unit, r, "Failed to apply SUID/SGID restrictions: %m"); + } + r = apply_restrict_namespaces(unit, context); if (r < 0) { *exit_status = EXIT_SECCOMP; @@ -3582,11 +3645,15 @@ static int exec_child( strv_free_and_replace(accum_env, ee); } - final_argv = replace_env_argv(command->argv, accum_env); - if (!final_argv) { - *exit_status = EXIT_MEMORY; - return log_oom(); - } + if (!FLAGS_SET(command->flags, EXEC_COMMAND_NO_ENV_EXPAND)) { + replaced_argv = replace_env_argv(command->argv, accum_env); + if (!replaced_argv) { + *exit_status = EXIT_MEMORY; + return log_oom(); + } + final_argv = replaced_argv; + } else + final_argv = command->argv; if (DEBUG_LOGGING) { _cleanup_free_ char *line; @@ -3852,6 +3919,8 @@ void exec_context_done(ExecContext *c) { c->stdin_data = mfree(c->stdin_data); c->stdin_data_size = 0; + + c->network_namespace_path = mfree(c->network_namespace_path); } int exec_context_destroy_runtime_directory(const ExecContext *c, const char *runtime_prefix) { @@ -3865,12 +3934,12 @@ int exec_context_destroy_runtime_directory(const ExecContext *c, const char *run STRV_FOREACH(i, c->directories[EXEC_DIRECTORY_RUNTIME].paths) { _cleanup_free_ char *p; - p = strjoin(runtime_prefix, "/", *i); + p = path_join(runtime_prefix, *i); if (!p) return -ENOMEM; - /* We execute this synchronously, since we need to be sure this is gone when we start the service - * next. */ + /* We execute this synchronously, since we need to be sure this is gone when we start the + * service next. */ (void) rm_rf(p, REMOVE_ROOT); } @@ -4111,17 +4180,23 @@ static bool tty_may_match_dev_console(const char *tty) { return true; /* if we could not resolve, assume it may */ /* "tty0" means the active VC, so it may be the same sometimes */ - return streq(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty)); + return path_equal(resolved, tty) || (streq(resolved, "tty0") && tty_is_vc(tty)); } -bool exec_context_may_touch_console(const ExecContext *ec) { +static bool exec_context_may_touch_tty(const ExecContext *ec) { + assert(ec); - return (ec->tty_reset || + return ec->tty_reset || ec->tty_vhangup || ec->tty_vt_disallocate || is_terminal_input(ec->std_input) || is_terminal_output(ec->std_output) || - is_terminal_output(ec->std_error)) && + is_terminal_output(ec->std_error); +} + +bool exec_context_may_touch_console(const ExecContext *ec) { + + return exec_context_may_touch_tty(ec) && tty_may_match_dev_console(exec_context_tty_path(ec)); } @@ -4163,7 +4238,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { "%sIgnoreSIGPIPE: %s\n" "%sMemoryDenyWriteExecute: %s\n" "%sRestrictRealtime: %s\n" - "%sKeyringMode: %s\n", + "%sRestrictSUIDSGID: %s\n" + "%sKeyringMode: %s\n" + "%sProtectHostname: %s\n", prefix, c->umask, prefix, c->working_directory ? c->working_directory : "/", prefix, c->root_directory ? c->root_directory : "/", @@ -4181,7 +4258,9 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { prefix, yes_no(c->ignore_sigpipe), prefix, yes_no(c->memory_deny_write_execute), prefix, yes_no(c->restrict_realtime), - prefix, exec_keyring_mode_to_string(c->keyring_mode)); + prefix, yes_no(c->restrict_suid_sgid), + prefix, exec_keyring_mode_to_string(c->keyring_mode), + prefix, yes_no(c->protect_hostname)); if (c->root_image) fprintf(f, "%sRootImage: %s\n", prefix, c->root_image); @@ -4527,6 +4606,11 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { prefix, s); } + if (c->network_namespace_path) + fprintf(f, + "%sNetworkNamespacePath: %s\n", + prefix, c->network_namespace_path); + if (c->syscall_errno > 0) { const char *errno_name; @@ -4538,11 +4622,6 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) { else fprintf(f, "%d\n", c->syscall_errno); } - - if (c->apparmor_profile) - fprintf(f, - "%sAppArmorProfile: %s%s\n", - prefix, c->apparmor_profile_ignore ? "-" : "", c->apparmor_profile); } bool exec_context_maintains_privileges(const ExecContext *c) { @@ -4586,6 +4665,30 @@ void exec_context_free_log_extra_fields(ExecContext *c) { c->n_log_extra_fields = 0; } +void exec_context_revert_tty(ExecContext *c) { + int r; + + assert(c); + + /* First, reset the TTY (possibly kicking everybody else from the TTY) */ + exec_context_tty_reset(c, NULL); + + /* And then undo what chown_terminal() did earlier. Note that we only do this if we have a path + * configured. If the TTY was passed to us as file descriptor we assume the TTY is opened and managed + * by whoever passed it to us and thus knows better when and how to chmod()/chown() it back. */ + + if (exec_context_may_touch_tty(c)) { + const char *path; + + path = exec_context_tty_path(c); + if (path) { + r = chmod_and_chown(path, TTY_MODE, 0, TTY_GID); + if (r < 0 && r != -ENOENT) + log_warning_errno(r, "Failed to reset TTY ownership/access mode of %s, ignoring: %m", path); + } + } +} + void exec_status_start(ExecStatus *s, pid_t pid) { assert(s); @@ -4610,12 +4713,8 @@ void exec_status_exit(ExecStatus *s, const ExecContext *context, pid_t pid, int s->code = code; s->status = status; - if (context) { - if (context->utmp_id) - (void) utmp_put_dead_process(context->utmp_id, pid, code, status); - - exec_context_tty_reset(context, NULL); - } + if (context && context->utmp_id) + (void) utmp_put_dead_process(context->utmp_id, pid, code, status); } void exec_status_reset(ExecStatus *s) { @@ -4831,18 +4930,23 @@ static ExecRuntime* exec_runtime_free(ExecRuntime *rt, bool destroy) { } static void exec_runtime_freep(ExecRuntime **rt) { - if (*rt) - (void) exec_runtime_free(*rt, false); + (void) exec_runtime_free(*rt, false); } -static int exec_runtime_allocate(ExecRuntime **rt) { - assert(rt); +static int exec_runtime_allocate(ExecRuntime **ret) { + ExecRuntime *n; - *rt = new0(ExecRuntime, 1); - if (!*rt) + assert(ret); + + n = new(ExecRuntime, 1); + if (!n) return -ENOMEM; - (*rt)->netns_storage_socket[0] = (*rt)->netns_storage_socket[1] = -1; + *n = (ExecRuntime) { + .netns_storage_socket = { -1, -1 }, + }; + + *ret = n; return 0; } @@ -4905,7 +5009,7 @@ static int exec_runtime_add( static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, ExecRuntime **ret) { _cleanup_free_ char *tmp_dir = NULL, *var_tmp_dir = NULL; - _cleanup_close_pair_ int netns_storage_socket[2] = {-1, -1}; + _cleanup_close_pair_ int netns_storage_socket[2] = { -1, -1 }; int r; assert(m); @@ -4913,7 +5017,7 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E assert(id); /* It is not necessary to create ExecRuntime object. */ - if (!c->private_network && !c->private_tmp) + if (!c->private_network && !c->private_tmp && !c->network_namespace_path) return 0; if (c->private_tmp) { @@ -4922,7 +5026,7 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E return r; } - if (c->private_network) { + if (c->private_network || c->network_namespace_path) { if (socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, netns_storage_socket) < 0) return -errno; } @@ -4932,8 +5036,7 @@ static int exec_runtime_make(Manager *m, const ExecContext *c, const char *id, E return r; /* Avoid cleanup */ - netns_storage_socket[0] = -1; - netns_storage_socket[1] = -1; + netns_storage_socket[0] = netns_storage_socket[1] = -1; return 1; } diff --git a/src/core/execute.h b/src/core/execute.h index 0f1bf5674..23bf3b546 100644 --- a/src/core/execute.h +++ b/src/core/execute.h @@ -19,6 +19,7 @@ typedef struct Manager Manager; #include "missing_resource.h" #include "namespace.h" #include "nsflags.h" +#include "time-util.h" #define EXEC_STDIN_DATA_MAX (64U*1024U*1024U) @@ -79,9 +80,9 @@ typedef enum 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 */ }; @@ -91,6 +92,7 @@ typedef enum ExecCommandFlags { EXEC_COMMAND_FULLY_PRIVILEGED = 1 << 1, EXEC_COMMAND_NO_SETUID = 1 << 2, EXEC_COMMAND_AMBIENT_MAGIC = 1 << 3, + EXEC_COMMAND_NO_ENV_EXPAND = 1 << 4, } ExecCommandFlags; /* Stores information about commands we execute. Covers both configuration settings as well as runtime data. */ @@ -147,8 +149,21 @@ struct ExecContext { struct rlimit *rlimit[_RLIMIT_MAX]; char *working_directory, *root_directory, *root_image; - bool working_directory_missing_ok; - bool working_directory_home; + bool working_directory_missing_ok:1; + bool working_directory_home:1; + + bool oom_score_adjust_set:1; + bool nice_set:1; + bool ioprio_set:1; + bool cpu_sched_set:1; + + /* This is not exposed to the user but available internally. We need it to make sure that whenever we + * spawn /usr/bin/mount it is run in the same process group as us so that the autofs logic detects + * that it belongs to us and we don't enter a trigger loop. */ + bool same_pgrp; + + bool cpu_sched_reset_on_fork; + bool non_blocking; mode_t umask; int oom_score_adjust; @@ -157,12 +172,13 @@ struct ExecContext { int cpu_sched_policy; int cpu_sched_priority; - cpu_set_t *cpuset; unsigned cpuset_ncpus; + cpu_set_t *cpuset; ExecInput std_input; ExecOutput std_output; ExecOutput std_error; + bool stdio_as_fds; char *stdio_fdname[3]; char *stdio_file[3]; @@ -171,8 +187,6 @@ struct ExecContext { nsec_t timer_slack_nsec; - bool stdio_as_fds; - char *tty_path; bool tty_reset; @@ -181,6 +195,8 @@ struct ExecContext { bool ignore_sigpipe; + ExecKeyringMode keyring_mode; + /* Since resolving these names might involve socket * connections and we don't want to deadlock ourselves these * names are resolved on execution only and in the child @@ -194,16 +210,15 @@ struct ExecContext { char *utmp_id; ExecUtmpMode utmp_mode; + bool no_new_privileges; + bool selinux_context_ignore; - char *selinux_context; - bool apparmor_profile_ignore; - char *apparmor_profile; - bool smack_process_label_ignore; - char *smack_process_label; - ExecKeyringMode keyring_mode; + char *selinux_context; + char *apparmor_profile; + char *smack_process_label; char **read_write_paths, **read_only_paths, **inaccessible_paths; unsigned long mount_flags; @@ -217,10 +232,8 @@ struct ExecContext { int secure_bits; int syslog_priority; - char *syslog_identifier; bool syslog_level_prefix; - - int log_level_max; + char *syslog_identifier; struct iovec* log_extra_fields; size_t n_log_extra_fields; @@ -228,34 +241,30 @@ struct ExecContext { usec_t log_rate_limit_interval_usec; unsigned log_rate_limit_burst; - bool cpu_sched_reset_on_fork; - bool non_blocking; + int log_level_max; + bool private_tmp; bool private_network; bool private_devices; bool private_users; bool private_mounts; - ProtectSystem protect_system; - ProtectHome protect_home; bool protect_kernel_tunables; bool protect_kernel_modules; bool protect_control_groups; + ProtectSystem protect_system; + ProtectHome protect_home; + bool protect_hostname; bool mount_apivfs; - bool no_new_privileges; - bool dynamic_user; bool remove_ipc; - /* This is not exposed to the user but available - * internally. We need it to make sure that whenever we spawn - * /usr/bin/mount it is run in the same process group as us so - * that the autofs logic detects that it belongs to us and we - * don't enter a trigger loop. */ - bool same_pgrp; + bool memory_deny_write_execute; + bool restrict_realtime; + bool restrict_suid_sgid; - unsigned long personality; bool lock_personality; + unsigned long personality; unsigned long restrict_namespaces; /* The CLONE_NEWxyz flags permitted to the unit's processes */ @@ -264,19 +273,13 @@ struct ExecContext { int syscall_errno; bool syscall_whitelist:1; - Set *address_families; bool address_families_whitelist:1; + Set *address_families; + + char *network_namespace_path; - ExecPreserveMode runtime_directory_preserve_mode; ExecDirectory directories[_EXEC_DIRECTORY_TYPE_MAX]; - - bool memory_deny_write_execute; - bool restrict_realtime; - - bool oom_score_adjust_set:1; - bool nice_set:1; - bool ioprio_set:1; - bool cpu_sched_set:1; + ExecPreserveMode runtime_directory_preserve_mode; }; static inline bool exec_context_restrict_namespaces_set(const ExecContext *c) { @@ -370,6 +373,8 @@ int exec_context_get_effective_ioprio(const ExecContext *c); void exec_context_free_log_extra_fields(ExecContext *c); +void exec_context_revert_tty(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); diff --git a/src/core/ima-setup.c b/src/core/ima-setup.c index fd7c5f64a..17072b09a 100644 --- a/src/core/ima-setup.c +++ b/src/core/ima-setup.c @@ -5,6 +5,9 @@ ***/ #include +#include +#include +#include #include #include "alloc-util.h" @@ -12,7 +15,6 @@ #include "fileio.h" #include "ima-setup.h" #include "log.h" -#include "util.h" #define IMA_SECFS_DIR "/sys/kernel/security/ima" #define IMA_SECFS_POLICY IMA_SECFS_DIR "/policy" diff --git a/src/core/job.c b/src/core/job.c index 59bb9d216..81f5f9cb7 100644 --- a/src/core/job.c +++ b/src/core/job.c @@ -17,6 +17,7 @@ #include "parse-util.h" #include "serialize.h" #include "set.h" +#include "sort-util.h" #include "special.h" #include "stdio-util.h" #include "string-table.h" @@ -1027,6 +1028,31 @@ int job_finish_and_invalidate(Job *j, JobResult result, bool recursive, bool alr job_fail_dependencies(u, UNIT_CONFLICTED_BY); } + /* A special check to make sure we take down anything RequisiteOf if we + * aren't active. This is when the verify-active job merges with a + * satisfying job type, and then loses it's invalidation effect, as the + * result there is JOB_DONE for the start job we merged into, while we + * should be failing the depending job if the said unit isn't infact + * active. Oneshots are an example of this, where going directly from + * activating to inactive is success. + * + * This happens when you use ConditionXYZ= in a unit too, since in that + * case the job completes with the JOB_DONE result, but the unit never + * really becomes active. Note that such a case still involves merging: + * + * A start job waits for something else, and a verify-active comes in + * and merges in the installed job. Then, later, when it becomes + * runnable, it finishes with JOB_DONE result as execution on conditions + * not being met is skipped, breaking our dependency semantics. + * + * Also, depending on if start job waits or not, the merging may or may + * not happen (the verify-active job may trigger after it finishes), so + * you get undeterministic results without this check. + */ + if (result == JOB_DONE && recursive && !UNIT_IS_ACTIVE_OR_RELOADING(unit_active_state(u))) { + if (IN_SET(t, JOB_START, JOB_RELOAD)) + job_fail_dependencies(u, UNIT_REQUISITE_OF); + } /* Trigger OnFailure dependencies that are not generated by * the unit itself. We don't treat JOB_CANCELED as failure in * this context. And JOB_FAILURE is already handled by the diff --git a/src/core/kill.h b/src/core/kill.h index f3915be1d..43648b6d8 100644 --- a/src/core/kill.h +++ b/src/core/kill.h @@ -22,9 +22,9 @@ struct KillContext { KillMode kill_mode; int kill_signal; int final_kill_signal; + int watchdog_signal; bool send_sigkill; bool send_sighup; - int watchdog_signal; }; typedef enum KillWho { diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index cdbc67f88..7fb1e6cc8 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -74,6 +74,7 @@ $1.SystemCallErrorNumber, config_parse_syscall_errno, 0, $1.MemoryDenyWriteExecute, config_parse_bool, 0, offsetof($1, exec_context.memory_deny_write_execute) $1.RestrictNamespaces, config_parse_restrict_namespaces, 0, offsetof($1, exec_context) $1.RestrictRealtime, config_parse_bool, 0, offsetof($1, exec_context.restrict_realtime) +$1.RestrictSUIDSGID, config_parse_bool, 0, offsetof($1, exec_context.restrict_suid_sgid) $1.RestrictAddressFamilies, config_parse_address_families, 0, offsetof($1, exec_context) $1.LockPersonality, config_parse_bool, 0, offsetof($1, exec_context.lock_personality)', `$1.SystemCallFilter, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 @@ -82,6 +83,7 @@ $1.SystemCallErrorNumber, config_parse_warn_compat, DISABLED_CO $1.MemoryDenyWriteExecute, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.RestrictNamespaces, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.RestrictRealtime, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 +$1.RestrictSUIDSGID, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.RestrictAddressFamilies, config_parse_warn_compat, DISABLED_CONFIGURATION, 0 $1.LockPersonality, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') $1.LimitCPU, config_parse_rlimit, RLIMIT_CPU, offsetof($1, exec_context.rlimit) @@ -114,6 +116,7 @@ $1.PrivateDevices, config_parse_bool, 0, $1.ProtectKernelTunables, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_tunables) $1.ProtectKernelModules, config_parse_bool, 0, offsetof($1, exec_context.protect_kernel_modules) $1.ProtectControlGroups, config_parse_bool, 0, offsetof($1, exec_context.protect_control_groups) +$1.NetworkNamespacePath, config_parse_unit_path_printf, 0, offsetof($1, exec_context.network_namespace_path) $1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network) $1.PrivateUsers, config_parse_bool, 0, offsetof($1, exec_context.private_users) $1.PrivateMounts, config_parse_bool, 0, offsetof($1, exec_context.private_mounts) @@ -133,6 +136,7 @@ $1.LogsDirectoryMode, config_parse_mode, 0, $1.LogsDirectory, config_parse_exec_directories, 0, offsetof($1, exec_context.directories[EXEC_DIRECTORY_LOGS].paths) $1.ConfigurationDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].mode) $1.ConfigurationDirectory, config_parse_exec_directories, 0, offsetof($1, exec_context.directories[EXEC_DIRECTORY_CONFIGURATION].paths) +$1.ProtectHostname, config_parse_bool, 0, offsetof($1, exec_context.protect_hostname) m4_ifdef(`HAVE_PAM', `$1.PAMName, config_parse_unit_string_printf, 0, offsetof($1, exec_context.pam_name)', `$1.PAMName, config_parse_warn_compat, DISABLED_CONFIGURATION, 0') @@ -165,6 +169,7 @@ $1.StartupCPUWeight, config_parse_cg_weight, 0, $1.CPUShares, config_parse_cpu_shares, 0, offsetof($1, cgroup_context.cpu_shares) $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.CPUQuotaPeriodSec, config_parse_sec_def_infinity, 0, offsetof($1, cgroup_context.cpu_quota_period_usec) $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) @@ -424,12 +429,14 @@ EXEC_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl CGROUP_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl KILL_CONTEXT_CONFIG_ITEMS(Swap)m4_dnl m4_dnl -Timer.OnCalendar, config_parse_timer, 0, 0 -Timer.OnActiveSec, config_parse_timer, 0, 0 -Timer.OnBootSec, config_parse_timer, 0, 0 -Timer.OnStartupSec, config_parse_timer, 0, 0 -Timer.OnUnitActiveSec, config_parse_timer, 0, 0 -Timer.OnUnitInactiveSec, config_parse_timer, 0, 0 +Timer.OnCalendar, config_parse_timer, TIMER_CALENDAR, 0 +Timer.OnActiveSec, config_parse_timer, TIMER_ACTIVE, 0 +Timer.OnBootSec, config_parse_timer, TIMER_BOOT, 0 +Timer.OnStartupSec, config_parse_timer, TIMER_STARTUP, 0 +Timer.OnUnitActiveSec, config_parse_timer, TIMER_UNIT_ACTIVE, 0 +Timer.OnUnitInactiveSec, config_parse_timer, TIMER_UNIT_INACTIVE, 0 +Timer.OnClockChange, config_parse_bool, 0, offsetof(Timer, on_clock_change) +Timer.OnTimezoneChange, config_parse_bool, 0, offsetof(Timer, on_timezone_change) Timer.Persistent, config_parse_bool, 0, offsetof(Timer, persistent) Timer.WakeSystem, config_parse_bool, 0, offsetof(Timer, wake_system) Timer.RemainAfterElapse, config_parse_bool, 0, offsetof(Timer, remain_after_elapse) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 44f00a304..9b7c8cba6 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -36,10 +36,12 @@ #include "ioprio.h" #include "ip-protocol-list.h" #include "journal-util.h" +#include "limits-util.h" #include "load-fragment.h" #include "log.h" #include "missing.h" #include "mountpoint-util.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -54,6 +56,7 @@ #include "unit-name.h" #include "unit-printf.h" #include "user-util.h" +#include "time-util.h" #include "web-util.h" static int parse_socket_protocol(const char *s) { @@ -309,23 +312,50 @@ int config_parse_unit_path_strv_printf( if (r < 0) return 0; - r = strv_push(x, k); + r = strv_consume(x, TAKE_PTR(k)); if (r < 0) return log_oom(); - k = NULL; } } -int config_parse_socket_listen(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +static int patch_var_run( + const char *unit, + const char *filename, + unsigned line, + const char *lvalue, + char **path) { + + const char *e; + char *z; + + e = path_startswith(*path, "/var/run/"); + if (!e) + return 0; + + z = path_join("/run/", e); + if (!z) + return log_oom(); + + log_syntax(unit, LOG_NOTICE, filename, line, 0, + "%s= references a path below legacy directory /var/run/, updating %s → %s; " + "please update the unit file accordingly.", lvalue, *path, z); + + free_and_replace(*path, z); + + return 1; +} + +int config_parse_socket_listen( + 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_ SocketPort *p = NULL; SocketPort *tail; @@ -362,6 +392,12 @@ int config_parse_socket_listen(const char *unit, if (r < 0) return 0; + if (ltype == SOCKET_FIFO) { + r = patch_var_run(unit, filename, line, lvalue, &k); + if (r < 0) + return r; + } + free_and_replace(p->path, k); p->type = ltype; @@ -391,6 +427,12 @@ int config_parse_socket_listen(const char *unit, return 0; } + if (k[0] == '/') { /* Only for AF_UNIX file system sockets… */ + r = patch_var_run(unit, filename, line, lvalue, &k); + if (r < 0) + return r; + } + r = socket_address_parse_and_warn(&p->address, k); if (r < 0) { if (r != -EAFNOSUPPORT) @@ -560,7 +602,8 @@ int config_parse_exec( for (;;) { /* We accept an absolute path as first argument. If it's prefixed with - and the path doesn't * exist, we ignore it instead of erroring out; if it's prefixed with @, we allow overriding of - * argv[0]; if it's prefixed with +, it will be run with full privileges and no sandboxing; if + * argv[0]; if it's prefixed with :, we will not do environment variable substitution; + * if it's prefixed with +, it will be run with full privileges and no sandboxing; if * it's prefixed with '!' we apply sandboxing, but do not change user/group credentials; if * it's prefixed with '!!', then we apply user/group credentials if the kernel supports ambient * capabilities -- if it doesn't we don't apply the credentials themselves, but do apply most @@ -575,6 +618,8 @@ int config_parse_exec( ignore = true; } else if (*f == '@' && !separate_argv0) separate_argv0 = true; + else if (*f == ':' && !(flags & EXEC_COMMAND_NO_ENV_EXPAND)) + flags |= EXEC_COMMAND_NO_ENV_EXPAND; else if (*f == '+' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) flags |= EXEC_COMMAND_FULLY_PRIVILEGED; else if (*f == '!' && !(flags & (EXEC_COMMAND_FULLY_PRIVILEGED|EXEC_COMMAND_NO_SETUID|EXEC_COMMAND_AMBIENT_MAGIC))) @@ -1453,24 +1498,24 @@ int config_parse_exec_smack_process_label( return 0; } -int config_parse_timer(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_timer( + 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_(calendar_spec_freep) CalendarSpec *c = NULL; + _cleanup_free_ char *k = NULL; + Unit *u = userdata; Timer *t = data; usec_t usec = 0; TimerValue *v; - TimerBase b; - _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; - Unit *u = userdata; - _cleanup_free_ char *k = NULL; int r; assert(filename); @@ -1484,36 +1529,35 @@ int config_parse_timer(const char *unit, return 0; } - b = timer_base_from_string(lvalue); - if (b < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer base, ignoring: %s", lvalue); - return 0; - } - r = unit_full_printf(u, rvalue, &k); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, "Failed to resolve unit specifiers in '%s', ignoring: %m", rvalue); return 0; } - if (b == TIMER_CALENDAR) { - if (calendar_spec_from_string(k, &c) < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse calendar specification, ignoring: %s", k); + if (ltype == TIMER_CALENDAR) { + r = calendar_spec_from_string(k, &c); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse calendar specification, ignoring: %s", k); return 0; } - } else - if (parse_sec(k, &usec) < 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse timer value, ignoring: %s", k); + } else { + r = parse_sec(k, &usec); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse timer value, ignoring: %s", k); return 0; } + } - v = new0(TimerValue, 1); + v = new(TimerValue, 1); if (!v) return log_oom(); - v->base = b; - v->value = usec; - v->calendar_spec = TAKE_PTR(c); + *v = (TimerValue) { + .base = ltype, + .value = usec, + .calendar_spec = TAKE_PTR(c), + }; LIST_PREPEND(value, t->values, v); @@ -2508,6 +2552,8 @@ int config_parse_unit_condition_null( assert(rvalue); assert(data); + log_syntax(unit, LOG_WARNING, filename, line, 0, "%s= is deprecated, please do not use.", lvalue); + if (isempty(rvalue)) { /* Empty assignment resets the list */ *list = condition_free_list(*list); @@ -2687,7 +2733,11 @@ int config_parse_syscall_filter( c->syscall_whitelist = true; /* Accept default syscalls if we are on a whitelist */ - r = seccomp_parse_syscall_filter("@default", -1, c->syscall_filter, SECCOMP_PARSE_WHITELIST); + r = seccomp_parse_syscall_filter( + "@default", -1, c->syscall_filter, + SECCOMP_PARSE_PERMISSIVE|SECCOMP_PARSE_WHITELIST, + unit, + NULL, 0); if (r < 0) return r; } @@ -2714,9 +2764,12 @@ int config_parse_syscall_filter( continue; } - r = seccomp_parse_syscall_filter_full(name, num, c->syscall_filter, - SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE|(invert ? SECCOMP_PARSE_INVERT : 0)|(c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0), - unit, filename, line); + r = seccomp_parse_syscall_filter( + name, num, c->syscall_filter, + SECCOMP_PARSE_LOG|SECCOMP_PARSE_PERMISSIVE| + (invert ? SECCOMP_PARSE_INVERT : 0)| + (c->syscall_whitelist ? SECCOMP_PARSE_WHITELIST : 0), + unit, filename, line); if (r < 0) return r; } @@ -4246,7 +4299,6 @@ int config_parse_pid_file( _cleanup_free_ char *k = NULL, *n = NULL; Unit *u = userdata; char **s = data; - const char *e; int r; assert(filename); @@ -4276,20 +4328,11 @@ int config_parse_pid_file( 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); + r = patch_var_run(unit, filename, line, lvalue, &n); + if (r < 0) + return r; + free_and_replace(*s, n); return 0; } diff --git a/src/core/loopback-setup.c b/src/core/loopback-setup.c index f613db83c..2a2fb8b59 100644 --- a/src/core/loopback-setup.c +++ b/src/core/loopback-setup.c @@ -8,6 +8,7 @@ #include "loopback-setup.h" #include "missing.h" #include "netlink-util.h" +#include "time-util.h" #define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC) diff --git a/src/core/machine-id-setup.c b/src/core/machine-id-setup.c index aae548064..9d3096e3a 100644 --- a/src/core/machine-id-setup.c +++ b/src/core/machine-id-setup.c @@ -16,6 +16,7 @@ #include "macro.h" #include "mkdir.h" #include "mountpoint-util.h" +#include "namespace-util.h" #include "path-util.h" #include "process-util.h" #include "stat-util.h" diff --git a/src/core/macros.systemd.in b/src/core/macros.systemd.in index d9e7f573b..9b310c484 100644 --- a/src/core/macros.systemd.in +++ b/src/core/macros.systemd.in @@ -40,41 +40,48 @@ OrderWithRequires(preun): systemd \ OrderWithRequires(postun): systemd \ %{nil} +%__systemd_someargs_0() %{error:This macro requires some arguments} +%__systemd_twoargs_2() %{nil} + %systemd_post() \ -if [ $1 -eq 1 ] ; then \ +%{expand:%%{?__systemd_someargs_%#}} \ +if [ $1 -eq 1 ] && [ -x @bindir@/systemctl ] ; then \ # Initial installation \ - systemctl --no-reload preset %{?*} >/dev/null 2>&1 || : \ + @bindir@/systemctl --no-reload preset %{?*} || : \ fi \ %{nil} %systemd_user_post() %{expand:%systemd_post \\--global %%{?*}} %systemd_preun() \ -if [ $1 -eq 0 ] ; then \ +%{expand:%%{?__systemd_someargs_%#}} \ +if [ $1 -eq 0 ] && [ -x @bindir@/systemctl ] ; then \ # Package removal, not upgrade \ - systemctl --no-reload disable --now %{?*} >/dev/null 2>&1 || : \ + @bindir@/systemctl --no-reload disable --now %{?*} || : \ fi \ %{nil} %systemd_user_preun() \ -if [ $1 -eq 0 ] ; then \ +%{expand:%%{?__systemd_someargs_%#}} \ +if [ $1 -eq 0 ] && [ -x @bindir@/systemctl ] ; then \ # Package removal, not upgrade \ - systemctl --global disable %{?*} >/dev/null 2>&1 || : \ + @bindir@/systemctl --global disable %{?*} || : \ fi \ %{nil} -%systemd_postun() %{nil} +%systemd_postun() %{expand:%%{?__systemd_someargs_%#}}%{nil} -%systemd_user_postun() %{nil} +%systemd_user_postun() %{expand:%%{?__systemd_someargs_%#}}%{nil} %systemd_postun_with_restart() \ -if [ $1 -ge 1 ] ; then \ +%{expand:%%{?__systemd_someargs_%#}} \ +if [ $1 -ge 1 ] && [ -x @bindir@/systemctl ] ; then \ # Package upgrade, not uninstall \ - systemctl try-restart %{?*} >/dev/null 2>&1 || : \ + @bindir@/systemctl try-restart %{?*} || : \ fi \ %{nil} -%systemd_user_postun_with_restart() %{nil} +%systemd_user_postun_with_restart() %{expand:%%{?__systemd_someargs_%#}}%{nil} %udev_hwdb_update() %{nil} @@ -84,16 +91,18 @@ fi \ # Deprecated. Use %tmpfiles_create_package instead %tmpfiles_create() \ -systemd-tmpfiles --create %{?*} >/dev/null 2>&1 || : \ +%{expand:%%{?__systemd_someargs_%#}} \ +[ -x @bindir@/systemd-tmpfiles ] && @bindir@/systemd-tmpfiles --create %{?*} || : \ %{nil} # Deprecated. Use %sysusers_create_package instead %sysusers_create() \ -systemd-sysusers %{?*} >/dev/null 2>&1 || : \ +%{expand:%%{?__systemd_someargs_%#}} \ +[ -x @bindir@/systemd-sysusers ] && @bindir@/systemd-sysusers %{?*} || : \ %{nil} %sysusers_create_inline() \ -systemd-sysusers - </dev/null 2>&1 || : \ +[ -x @bindir@/systemd-sysusers ] && @bindir@/systemd-sysusers - </dev/null 2>&1 || : \ %(cat %2) \ SYSTEMD_INLINE_EOF \ @@ -131,15 +141,18 @@ SYSTEMD_INLINE_EOF \ # %files # %{_tmpfilesdir}/%{name}.conf %tmpfiles_create_package() \ +%{expand:%%{?!__systemd_twoargs_%#:%%{error:This macro requires two arguments}}} \ systemd-tmpfiles --replace=%_tmpfilesdir/%1.conf --create - </dev/null 2>&1 || : \ %(cat %2) \ SYSTEMD_INLINE_EOF \ %{nil} %sysctl_apply() \ -@rootlibexecdir@/systemd-sysctl %{?*} >/dev/null 2>&1 || : \ +%{expand:%%{?__systemd_someargs_%#}} \ +[ -x @rootlibexecdir@/systemd-sysctl ] && @rootlibexecdir@/systemd-sysctl %{?*} || : \ %{nil} %binfmt_apply() \ -@rootlibexecdir@/systemd-binfmt %{?*} >/dev/null 2>&1 || : \ +%{expand:%%{?__systemd_someargs_%#}} \ +[ -x @rootlibexecdir@/systemd-binfmt ] && @rootlibexecdir@/systemd-binfmt %{?*} || : \ %{nil} diff --git a/src/core/main.c b/src/core/main.c index 561f956f0..46db47126 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -47,6 +47,7 @@ #include "ima-setup.h" #include "killall.h" #include "kmod-setup.h" +#include "limits-util.h" #include "load-fragment.h" #include "log.h" #include "loopback-setup.h" @@ -137,11 +138,11 @@ static EmergencyAction arg_cad_burst_action = EMERGENCY_ACTION_REBOOT_FORCE; _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 we are running in a container, 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); + _exit(EXIT_EXCEPTION); } if (arg_crash_reboot) { @@ -1382,7 +1383,7 @@ static int bump_rlimit_memlock(struct rlimit *saved_rlimit) { static void test_usr(void) { - /* Check that /usr is not a separate fs */ + /* Check that /usr is either on the same file system as / or mounted already. */ if (dir_is_empty("/usr") <= 0) return; @@ -1907,9 +1908,8 @@ static int invoke_main_loop( *ret_shutdown_verb = NULL; /* Steal the switch root parameters */ - *ret_switch_root_dir = m->switch_root; - *ret_switch_root_init = m->switch_root_init; - m->switch_root = m->switch_root_init = NULL; + *ret_switch_root_dir = TAKE_PTR(m->switch_root); + *ret_switch_root_init = TAKE_PTR(m->switch_root_init); return 0; @@ -2120,13 +2120,13 @@ static int do_queue_default_job( assert(target->load_state == UNIT_LOADED); - r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, &error, &default_unit_job); + r = manager_add_job(m, JOB_START, target, JOB_ISOLATE, NULL, &error, &default_unit_job); if (r == -EPERM) { log_debug_errno(r, "Default target could not be isolated, starting instead: %s", bus_error_message(&error, r)); sd_bus_error_free(&error); - r = manager_add_job(m, JOB_START, target, JOB_REPLACE, &error, &default_unit_job); + r = manager_add_job(m, JOB_START, target, JOB_REPLACE, NULL, &error, &default_unit_job); if (r < 0) { *ret_error_message = "Failed to start default target"; return log_emergency_errno(r, "Failed to start default target: %s", bus_error_message(&error, r)); @@ -2379,8 +2379,7 @@ int main(int argc, char *argv[]) { (void) prctl(PR_SET_NAME, systemd); /* Save the original command line */ - saved_argv = argv; - saved_argc = argc; + save_argc_argv(argc, argv); /* Make sure that if the user says "syslog" we actually log to the journal. */ log_set_upgrade_syslog_to_journal(true); diff --git a/src/core/manager.c b/src/core/manager.c index 6086531ba..12ae911a3 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -52,11 +52,13 @@ #include "log.h" #include "macro.h" #include "manager.h" +#include "memory-util.h" #include "missing.h" #include "mkdir.h" #include "parse-util.h" #include "path-lookup.h" #include "path-util.h" +#include "plymouth-util.h" #include "process-util.h" #include "ratelimit.h" #include "rlimit-util.h" @@ -77,7 +79,6 @@ #include "umask-util.h" #include "unit-name.h" #include "user-util.h" -#include "util.h" #include "virt.h" #include "watchdog.h" @@ -291,10 +292,10 @@ static int manager_check_ask_password(Manager *m) { m->ask_password_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); if (m->ask_password_inotify_fd < 0) - return log_error_errno(errno, "inotify_init1() failed: %m"); + return log_error_errno(errno, "Failed to create inotify object: %m"); if (inotify_add_watch(m->ask_password_inotify_fd, "/run/systemd/ask-password", IN_CREATE|IN_DELETE|IN_MOVE) < 0) { - log_error_errno(errno, "Failed to add watch on /run/systemd/ask-password: %m"); + log_error_errno(errno, "Failed to watch \"/run/systemd/ask-password\": %m"); manager_close_ask_password(m); return -errno; } @@ -587,6 +588,7 @@ static char** sanitize_environment(char **l) { "MAINPID", "MANAGERPID", "NOTIFY_SOCKET", + "PIDFILE", "REMOTE_ADDR", "REMOTE_PORT", "SERVICE_RESULT", @@ -1251,7 +1253,7 @@ static unsigned manager_dispatch_stop_when_unneeded_queue(Manager *m) { } /* Ok, nobody needs us anymore. Sniff. Then let's commit suicide */ - r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); + r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, NULL, &error, NULL); if (r < 0) log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); } @@ -1617,6 +1619,8 @@ static void manager_ready(Manager *m) { /* Let's finally catch up with any changes that took place while we were reloading/reexecing */ manager_catchup(m); + + m->honor_device_enumeration = true; } static Manager* manager_reloading_start(Manager *m) { @@ -1728,9 +1732,17 @@ int manager_startup(Manager *m, FILE *serialization, FDSet *fds) { return 0; } -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret) { - int r; +int manager_add_job( + Manager *m, + JobType type, + Unit *unit, + JobMode mode, + Set *affected_jobs, + sd_bus_error *error, + Job **ret) { + Transaction *tr; + int r; assert(m); assert(type < _JOB_TYPE_MAX); @@ -1738,10 +1750,10 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e assert(mode < _JOB_MODE_MAX); if (mode == JOB_ISOLATE && type != JOB_START) - return sd_bus_error_setf(e, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Isolate is only valid for start."); if (mode == JOB_ISOLATE && !unit->allow_isolate) - return sd_bus_error_setf(e, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); + return sd_bus_error_setf(error, BUS_ERROR_NO_ISOLATION, "Operation refused, unit may not be isolated."); log_unit_debug(unit, "Trying to enqueue job %s/%s/%s", unit->id, job_type_to_string(type), job_mode_to_string(mode)); @@ -1753,7 +1765,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e r = transaction_add_job_and_dependencies(tr, type, unit, NULL, true, false, IN_SET(mode, JOB_IGNORE_DEPENDENCIES, JOB_IGNORE_REQUIREMENTS), - mode == JOB_IGNORE_DEPENDENCIES, e); + mode == JOB_IGNORE_DEPENDENCIES, error); if (r < 0) goto tr_abort; @@ -1763,7 +1775,7 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e goto tr_abort; } - r = transaction_activate(tr, m, mode, e); + r = transaction_activate(tr, m, mode, affected_jobs, error); if (r < 0) goto tr_abort; @@ -1771,8 +1783,8 @@ int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_e "Enqueued job %s/%s as %u", unit->id, job_type_to_string(type), (unsigned) tr->anchor_job->id); - if (_ret) - *_ret = tr->anchor_job; + if (ret) + *ret = tr->anchor_job; transaction_free(tr); return 0; @@ -1783,7 +1795,7 @@ tr_abort: return r; } -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **ret) { +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, sd_bus_error *e, Job **ret) { Unit *unit = NULL; /* just to appease gcc, initialization is not really necessary */ int r; @@ -1797,10 +1809,10 @@ int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode return r; assert(unit); - return manager_add_job(m, type, unit, mode, e, ret); + return manager_add_job(m, type, unit, mode, affected_jobs, e, ret); } -int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret) { +int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, Job **ret) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; int r; @@ -1809,7 +1821,7 @@ int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, assert(name); assert(mode < _JOB_MODE_MAX); - r = manager_add_job_by_name(m, type, name, mode, &error, ret); + r = manager_add_job_by_name(m, type, name, mode, affected_jobs, &error, ret); if (r < 0) return log_warning_errno(r, "Failed to enqueue %s job for %s: %s", job_mode_to_string(mode), name, bus_error_message(&error, r)); @@ -1837,7 +1849,7 @@ int manager_propagate_reload(Manager *m, Unit *unit, JobMode mode, sd_bus_error /* Failure in adding individual dependencies is ignored, so this always succeeds. */ transaction_add_propagate_reload_jobs(tr, unit, tr->anchor_job, mode == JOB_IGNORE_DEPENDENCIES, e); - r = transaction_activate(tr, m, mode, e); + r = transaction_activate(tr, m, mode, NULL, e); if (r < 0) goto tr_abort; @@ -2127,6 +2139,16 @@ void manager_clear_jobs(Manager *m) { job_finish_and_invalidate(j, JOB_CANCELED, false, false); } +void manager_unwatch_pid(Manager *m, pid_t pid) { + assert(m); + + /* First let's drop the unit keyed as "pid". */ + (void) hashmap_remove(m->watch_pids, PID_TO_PTR(pid)); + + /* Then, let's also drop the array keyed by -pid. */ + free(hashmap_remove(m->watch_pids, PID_TO_PTR(-pid))); +} + static int manager_dispatch_run_queue(sd_event_source *source, void *userdata) { Manager *m = userdata; Job *j; @@ -2537,7 +2559,7 @@ static void manager_start_target(Manager *m, const char *name, JobMode mode) { log_debug("Activating special unit %s", name); - r = manager_add_job_by_name(m, JOB_START, name, mode, &error, NULL); + r = manager_add_job_by_name(m, JOB_START, name, mode, NULL, &error, NULL); if (r < 0) log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r)); } @@ -3054,7 +3076,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { } if (connect(fd, &sa.sa, SOCKADDR_UN_LEN(sa.un)) < 0) { - if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) + if (!IN_SET(errno, EAGAIN, ENOENT) && !ERRNO_IS_DISCONNECT(errno)) log_error_errno(errno, "connect() failed: %m"); return; } @@ -3066,7 +3088,7 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) { errno = 0; if (write(fd, message, n + 1) != n + 1) - if (!IN_SET(errno, EPIPE, EAGAIN, ENOENT, ECONNREFUSED, ECONNRESET, ECONNABORTED)) + if (!IN_SET(errno, EAGAIN, ENOENT) && !ERRNO_IS_DISCONNECT(errno)) log_error_errno(errno, "Failed to write Plymouth message: %m"); } @@ -3129,6 +3151,9 @@ int manager_serialize( (void) serialize_bool(f, "taint-logged", m->taint_logged); (void) serialize_bool(f, "service-watchdogs", m->service_watchdogs); + /* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */ + (void) serialize_bool(f, "honor-device-enumeration", !switching_root); + t = show_status_to_string(m->show_status); if (t) (void) serialize_item(f, "show-status", t); @@ -3357,6 +3382,15 @@ int manager_deserialize(Manager *m, FILE *f, FDSet *fds) { else m->service_watchdogs = b; + } else if ((val = startswith(l, "honor-device-enumeration="))) { + int b; + + b = parse_boolean(val); + if (b < 0) + log_notice("Failed to parse honor-device-enumeration flag '%s', ignoring.", val); + else + m->honor_device_enumeration = b; + } else if ((val = startswith(l, "show-status="))) { ShowStatus s; @@ -3547,6 +3581,11 @@ int manager_reload(Manager *m) { assert(m->n_reloading > 0); m->n_reloading--; + /* On manager reloading, device tag data should exists, thus, we should honor the results of device + * enumeration. The flag should be always set correctly by the serialized data, but it may fail. So, + * let's always set the flag here for safety. */ + m->honor_device_enumeration = true; + manager_ready(m); m->send_reloading_done = true; @@ -3775,7 +3814,7 @@ static bool generator_path_any(const char* const* paths) { return found; } -static const char* system_env_generator_binary_paths[] = { +static const char *const system_env_generator_binary_paths[] = { "/run/systemd/system-environment-generators", "/etc/systemd/system-environment-generators", "/usr/local/lib/systemd/system-environment-generators", @@ -3783,7 +3822,7 @@ static const char* system_env_generator_binary_paths[] = { NULL }; -static const char* user_env_generator_binary_paths[] = { +static const char *const user_env_generator_binary_paths[] = { "/run/systemd/user-environment-generators", "/etc/systemd/user-environment-generators", "/usr/local/lib/systemd/user-environment-generators", @@ -3793,7 +3832,7 @@ 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; + const char *const *paths; void* args[] = { [STDOUT_GENERATE] = &tmp, [STDOUT_COLLECT] = &tmp, @@ -3810,8 +3849,8 @@ static int manager_run_environment_generators(Manager *m) { return 0; RUN_WITH_UMASK(0022) - r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, m->transient_environment); - + r = execute_directories(paths, DEFAULT_TIMEOUT_USEC, gather_environment, + args, NULL, m->transient_environment, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); return r; } @@ -3845,8 +3884,8 @@ static int manager_run_generators(Manager *m) { argv[4] = NULL; RUN_WITH_UMASK(0022) - (void) execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, - NULL, NULL, (char**) argv, m->transient_environment); + (void) execute_directories((const char* const*) paths, DEFAULT_TIMEOUT_USEC, NULL, NULL, + (char**) argv, m->transient_environment, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); r = 0; diff --git a/src/core/manager.h b/src/core/manager.h index bce8020cf..cdd4882a6 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -395,6 +395,8 @@ struct Manager { * multiple times on the same unit. */ unsigned sigchldgen; unsigned notifygen; + + bool honor_device_enumeration; }; #define MANAGER_IS_SYSTEM(m) ((m)->unit_file_scope == UNIT_FILE_SYSTEM) @@ -425,9 +427,9 @@ int manager_load_unit(Manager *m, const char *name, const char *path, sd_bus_err int manager_load_startable_unit_or_warn(Manager *m, const char *name, const char *path, Unit **ret); int manager_load_unit_from_dbus_path(Manager *m, const char *s, sd_bus_error *e, Unit **_u); -int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, sd_bus_error *e, Job **_ret); -int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, sd_bus_error *e, Job **_ret); -int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Job **ret); +int manager_add_job(Manager *m, JobType type, Unit *unit, JobMode mode, Set *affected_jobs, sd_bus_error *e, Job **_ret); +int manager_add_job_by_name(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, sd_bus_error *e, Job **_ret); +int manager_add_job_by_name_and_warn(Manager *m, JobType type, const char *name, JobMode mode, Set *affected_jobs, Job **ret); int manager_propagate_reload(Manager *m, Unit *unit, JobMode mode, sd_bus_error *e); void manager_dump_units(Manager *s, FILE *f, const char *prefix); @@ -437,6 +439,8 @@ int manager_get_dump_string(Manager *m, char **ret); void manager_clear_jobs(Manager *m); +void manager_unwatch_pid(Manager *m, pid_t pid); + unsigned manager_dispatch_load_queue(Manager *m); int manager_default_environment(Manager *m); diff --git a/src/core/meson.build b/src/core/meson.build index 85021bdc0..86ab35d81 100644 --- a/src/core/meson.build +++ b/src/core/meson.build @@ -1,6 +1,17 @@ # SPDX-License-Identifier: LGPL-2.1+ -libcore_la_sources = ''' +libcore_shared_sources = ''' + killall.c + killall.h + loopback-setup.c + loopback-setup.h + machine-id-setup.c + machine-id-setup.h + mount-setup.c + mount-setup.h +'''.split() + +libcore_sources = ''' audit-fd.c audit-fd.h automount.c @@ -69,8 +80,6 @@ libcore_la_sources = ''' job.h kill.c kill.h - killall.c - killall.h kmod-setup.c kmod-setup.h load-dropin.c @@ -79,14 +88,8 @@ libcore_la_sources = ''' load-fragment.h locale-setup.c locale-setup.h - loopback-setup.c - loopback-setup.h - machine-id-setup.c - machine-id-setup.h manager.c manager.h - mount-setup.c - mount-setup.h mount.c mount.h namespace.c @@ -144,13 +147,24 @@ load_fragment_gperf_nulstr_c = custom_target( command : [awk, '-f', '@INPUT0@', '@INPUT1@'], capture : true) +# A convenience library to share code with other binaries: +# systemd-shutdown, systemd-remount-fs, systemd-machine-id-setup, … +libcore_shared = static_library( + 'core-shared', + libcore_shared_sources, + include_directories : includes, + dependencies : [versiondep, + libmount]) + libcore = static_library( 'core', - libcore_la_sources, + libcore_sources, load_fragment_gperf_c, load_fragment_gperf_nulstr_c, include_directories : includes, - dependencies : [threads, + link_whole : libcore_shared, + dependencies : [versiondep, + threads, librt, libseccomp, libpam, @@ -162,16 +176,6 @@ libcore = static_library( systemd_sources = files('main.c') -systemd_shutdown_sources = files(''' - shutdown.c - umount.c - umount.h - mount-setup.c - mount-setup.h - killall.c - killall.h -'''.split()) - in_files = [['macros.systemd', rpmmacrosdir], ['system.conf', pkgsysconfdir], ['systemd.pc', pkgconfigdatadir], diff --git a/src/core/mount-setup.c b/src/core/mount-setup.c index 3ce6164b0..b97285e55 100644 --- a/src/core/mount-setup.c +++ b/src/core/mount-setup.c @@ -23,12 +23,12 @@ #include "mkdir.h" #include "mount-setup.h" #include "mountpoint-util.h" +#include "nulstr-util.h" #include "path-util.h" #include "set.h" #include "smack-util.h" #include "strv.h" #include "user-util.h" -#include "util.h" #include "virt.h" typedef enum MountMode { diff --git a/src/core/mount.c b/src/core/mount.c index c31cad6b5..b7fd35fc6 100644 --- a/src/core/mount.c +++ b/src/core/mount.c @@ -5,18 +5,16 @@ #include #include -#include - #include "sd-messages.h" #include "alloc-util.h" #include "dbus-mount.h" #include "dbus-unit.h" #include "device.h" -#include "escape.h" #include "exit-status.h" #include "format-util.h" #include "fstab-util.h" +#include "libmount-util.h" #include "log.h" #include "manager.h" #include "mkdir.h" @@ -36,9 +34,6 @@ #define RETRY_UMOUNT_MAX 32 -DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); - static const UnitActiveState state_translation_table[_MOUNT_STATE_MAX] = { [MOUNT_DEAD] = UNIT_INACTIVE, [MOUNT_MOUNTING] = UNIT_ACTIVATING, @@ -101,20 +96,6 @@ static bool mount_is_bind(const MountParameters *p) { return false; } -static bool mount_is_auto(const MountParameters *p) { - assert(p); - - return !fstab_test_option(p->options, "noauto\0"); -} - -static bool mount_is_automount(const MountParameters *p) { - assert(p); - - return fstab_test_option(p->options, - "comment=systemd.automount\0" - "x-systemd.automount\0"); -} - static bool mount_is_bound_to_device(const Mount *m) { const MountParameters *p; @@ -338,7 +319,6 @@ static int mount_add_mount_dependencies(Mount *m) { } static int mount_add_device_dependencies(Mount *m) { - bool device_wants_mount; UnitDependencyMask mask; MountParameters *p; UnitDependency dep; @@ -368,9 +348,6 @@ static int mount_add_device_dependencies(Mount *m) { if (path_equal(m->where, "/")) return 0; - 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 * unplugged. But the user can still force this dep with an @@ -381,7 +358,7 @@ static int mount_add_device_dependencies(Mount *m) { /* We always use 'what' from /proc/self/mountinfo if mounted */ mask = m->from_proc_self_mountinfo ? UNIT_DEPENDENCY_MOUNTINFO_IMPLICIT : UNIT_DEPENDENCY_FILE; - r = unit_add_node_dependency(UNIT(m), p->what, device_wants_mount, dep, mask); + r = unit_add_node_dependency(UNIT(m), p->what, false, dep, mask); if (r < 0) return r; @@ -722,7 +699,7 @@ static int mount_coldplug(Unit *u) { pid_is_unwaited(m->control_pid) && MOUNT_STATE_WITH_PROCESS(new_state)) { - r = unit_watch_pid(UNIT(m), m->control_pid); + r = unit_watch_pid(UNIT(m), m->control_pid, false); if (r < 0) return r; @@ -828,9 +805,8 @@ static int mount_spawn(Mount *m, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; - r = unit_watch_pid(UNIT(m), pid); + r = unit_watch_pid(UNIT(m), pid, true); if (r < 0) - /* FIXME: we need to do something here */ return r; *_pid = pid; @@ -1122,7 +1098,7 @@ static int mount_start(Unit *u) { assert(IN_SET(m->state, MOUNT_DEAD, MOUNT_FAILED)); - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { mount_enter_dead(m, MOUNT_FAILURE_START_LIMIT_HIT); return r; @@ -1639,7 +1615,6 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { for (;;) { struct libmnt_fs *fs; const char *device, *path, *options, *fstype; - _cleanup_free_ char *d = NULL, *p = NULL; int k; k = mnt_table_next_fs(t, i, &fs); @@ -1656,15 +1631,9 @@ static int mount_load_proc_self_mountinfo(Manager *m, bool set_flags) { if (!device || !path) continue; - if (cunescape(device, UNESCAPE_RELAX, &d) < 0) - return log_oom(); + device_found_node(m, device, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT); - if (cunescape(path, UNESCAPE_RELAX, &p) < 0) - return log_oom(); - - device_found_node(m, d, DEVICE_FOUND_MOUNT, DEVICE_FOUND_MOUNT); - - (void) mount_setup_unit(m, d, p, options, fstype, set_flags); + (void) mount_setup_unit(m, device, path, options, fstype, set_flags); } return 0; @@ -1938,7 +1907,7 @@ static int mount_kill(Unit *u, KillWho who, int signo, sd_bus_error *error) { assert(m); - return unit_kill_common(u, who, signo, -1, MOUNT(u)->control_pid, error); + return unit_kill_common(u, who, signo, -1, m->control_pid, error); } static int mount_control_pid(Unit *u) { diff --git a/src/core/namespace.c b/src/core/namespace.c index 7f553a42c..8475145f0 100644 --- a/src/core/namespace.c +++ b/src/core/namespace.c @@ -21,17 +21,19 @@ #include "mkdir.h" #include "mount-util.h" #include "mountpoint-util.h" +#include "namespace-util.h" #include "namespace.h" +#include "nulstr-util.h" #include "path-util.h" #include "selinux-util.h" #include "socket-util.h" +#include "sort-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "umask-util.h" #include "user-util.h" -#include "util.h" #define DEV_MOUNT_OPTIONS (MS_NOSUID|MS_STRICTATIME|MS_NOEXEC) @@ -49,6 +51,8 @@ typedef enum MountMode { READONLY, READWRITE, TMPFS, + READWRITE_IMPLICIT, /* Should have the lowest priority. */ + _MOUNT_MODE_MAX, } MountMode; typedef struct MountEntry { @@ -57,6 +61,7 @@ typedef struct MountEntry { bool ignore:1; /* Ignore if path does not exist? */ bool has_prefix:1; /* Already is prefixed by the root dir? */ bool read_only:1; /* Shall this mount point be read-only? */ + bool nosuid:1; /* Shall set MS_NOSUID on the mount itself */ bool applied:1; /* Already applied */ char *path_malloc; /* Use this instead of 'path_const' if we had to allocate memory */ const char *source_const; /* The source path, for bind mounts */ @@ -77,26 +82,26 @@ static const MountEntry apivfs_table[] = { /* ProtectKernelTunables= option and the related filesystem APIs */ static const MountEntry protect_kernel_tunables_table[] = { - { "/proc/acpi", READONLY, true }, - { "/proc/apm", READONLY, true }, /* Obsolete API, there's no point in permitting access to this, ever */ - { "/proc/asound", READONLY, true }, - { "/proc/bus", READONLY, true }, - { "/proc/fs", READONLY, true }, - { "/proc/irq", READONLY, true }, - { "/proc/kallsyms", INACCESSIBLE, true }, - { "/proc/kcore", INACCESSIBLE, true }, - { "/proc/latency_stats", READONLY, true }, - { "/proc/mtrr", READONLY, true }, - { "/proc/scsi", READONLY, true }, - { "/proc/sys", READONLY, false }, - { "/proc/sysrq-trigger", READONLY, true }, - { "/proc/timer_stats", READONLY, true }, - { "/sys", READONLY, false }, - { "/sys/fs/bpf", READONLY, true }, - { "/sys/fs/cgroup", READWRITE, false }, /* READONLY is set by ProtectControlGroups= option */ - { "/sys/fs/selinux", READWRITE, true }, - { "/sys/kernel/debug", READONLY, true }, - { "/sys/kernel/tracing", READONLY, true }, + { "/proc/acpi", READONLY, true }, + { "/proc/apm", READONLY, true }, /* Obsolete API, there's no point in permitting access to this, ever */ + { "/proc/asound", READONLY, true }, + { "/proc/bus", READONLY, true }, + { "/proc/fs", READONLY, true }, + { "/proc/irq", READONLY, true }, + { "/proc/kallsyms", INACCESSIBLE, true }, + { "/proc/kcore", INACCESSIBLE, true }, + { "/proc/latency_stats", READONLY, true }, + { "/proc/mtrr", READONLY, true }, + { "/proc/scsi", READONLY, true }, + { "/proc/sys", READONLY, false }, + { "/proc/sysrq-trigger", READONLY, true }, + { "/proc/timer_stats", READONLY, true }, + { "/sys", READONLY, false }, + { "/sys/fs/bpf", READONLY, true }, + { "/sys/fs/cgroup", READWRITE_IMPLICIT, false }, /* READONLY is set by ProtectControlGroups= option */ + { "/sys/fs/selinux", READWRITE_IMPLICIT, true }, + { "/sys/kernel/debug", READONLY, true }, + { "/sys/kernel/tracing", READONLY, true }, }; /* ProtectKernelModules= option */ @@ -171,15 +176,33 @@ static const MountEntry protect_system_full_table[] = { * shall manage those, orthogonally). */ static const MountEntry protect_system_strict_table[] = { - { "/", READONLY, false }, - { "/proc", READWRITE, false }, /* ProtectKernelTunables= */ - { "/sys", READWRITE, false }, /* ProtectKernelTunables= */ - { "/dev", READWRITE, false }, /* PrivateDevices= */ - { "/home", READWRITE, true }, /* ProtectHome= */ - { "/run/user", READWRITE, true }, /* ProtectHome= */ - { "/root", READWRITE, true }, /* ProtectHome= */ + { "/", READONLY, false }, + { "/proc", READWRITE_IMPLICIT, false }, /* ProtectKernelTunables= */ + { "/sys", READWRITE_IMPLICIT, false }, /* ProtectKernelTunables= */ + { "/dev", READWRITE_IMPLICIT, false }, /* PrivateDevices= */ + { "/home", READWRITE_IMPLICIT, true }, /* ProtectHome= */ + { "/run/user", READWRITE_IMPLICIT, true }, /* ProtectHome= */ + { "/root", READWRITE_IMPLICIT, true }, /* ProtectHome= */ }; +static const char * const mount_mode_table[_MOUNT_MODE_MAX] = { + [INACCESSIBLE] = "inaccessible", + [BIND_MOUNT] = "bind", + [BIND_MOUNT_RECURSIVE] = "rbind", + [PRIVATE_TMP] = "private-tmp", + [PRIVATE_DEV] = "private-dev", + [BIND_DEV] = "bind-dev", + [EMPTY_DIR] = "empty", + [SYSFS] = "sysfs", + [PROCFS] = "procfs", + [READONLY] = "read-only", + [READWRITE] = "read-write", + [TMPFS] = "tmpfs", + [READWRITE_IMPLICIT] = "rw-implicit", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_TO_STRING(mount_mode, MountMode); + static const char *mount_entry_path(const MountEntry *p) { assert(p); @@ -220,7 +243,7 @@ static int append_access_mounts(MountEntry **p, char **strv, MountMode mode, boo assert(p); - /* Adds a list of user-supplied READWRITE/READONLY/INACCESSIBLE entries */ + /* Adds a list of user-supplied READWRITE/READWRITE_IMPLICIT/READONLY/INACCESSIBLE entries */ STRV_FOREACH(i, strv) { bool ignore = false, needs_prefix = false; @@ -286,6 +309,7 @@ static int append_bind_mounts(MountEntry **p, const BindMount *binds, size_t n) .path_const = b->destination, .mode = b->recursive ? BIND_MOUNT_RECURSIVE : BIND_MOUNT, .read_only = b->read_only, + .nosuid = b->nosuid, .source_const = b->source, .ignore = b->ignore_enoent, }; @@ -446,7 +470,7 @@ static void drop_duplicates(MountEntry *m, size_t *n) { if (previous && path_equal(mount_entry_path(f), mount_entry_path(previous)) && !f->applied && !previous->applied) { - log_debug("%s is duplicate.", mount_entry_path(f)); + log_debug("%s (%s) is duplicate.", mount_entry_path(f), mount_mode_to_string(f->mode)); previous->read_only = previous->read_only || mount_entry_read_only(f); /* Propagate the read-only flag to the remaining entry */ mount_entry_done(f); continue; @@ -500,8 +524,8 @@ static void drop_nop(MountEntry *m, size_t *n) { for (f = m, t = m; f < m + *n; f++) { - /* Only suppress such subtrees for READONLY and READWRITE entries */ - if (IN_SET(f->mode, READONLY, READWRITE)) { + /* Only suppress such subtrees for READONLY, READWRITE and READWRITE_IMPLICIT entries */ + if (IN_SET(f->mode, READONLY, READWRITE, READWRITE_IMPLICIT)) { MountEntry *p; bool found = false; @@ -515,7 +539,9 @@ static void drop_nop(MountEntry *m, size_t *n) { /* We found it, let's see if it's the same mode, if so, we can drop this entry */ if (found && p->mode == f->mode) { - log_debug("%s is redundant by %s", mount_entry_path(f), mount_entry_path(p)); + log_debug("%s (%s) is made redundant by %s (%s)", + mount_entry_path(f), mount_mode_to_string(f->mode), + mount_entry_path(p), mount_mode_to_string(p->mode)); mount_entry_done(f); continue; } @@ -736,27 +762,27 @@ static int mount_private_dev(MountEntry *m) { goto fail; } - rmdir(dev); - rmdir(temporary_mount); + (void) rmdir(dev); + (void) rmdir(temporary_mount); return 0; fail: if (devpts) - umount(devpts); + (void) umount(devpts); if (devshm) - umount(devshm); + (void) umount(devshm); if (devhugepages) - umount(devhugepages); + (void) umount(devhugepages); if (devmqueue) - umount(devmqueue); + (void) umount(devmqueue); - umount(dev); - rmdir(dev); - rmdir(temporary_mount); + (void) umount(dev); + (void) rmdir(dev); + (void) rmdir(temporary_mount); return r; } @@ -908,6 +934,7 @@ static int apply_mount( case READONLY: case READWRITE: + case READWRITE_IMPLICIT: r = path_is_mount_point(mount_entry_path(m), root_directory, 0); if (r == -ENOENT && m->ignore) return 0; @@ -1017,47 +1044,56 @@ 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; +/* Change per-mount flags on an existing mount */ +static int bind_remount_one(const char *path, unsigned long orig_flags, unsigned long new_flags, unsigned long flags_mask) { + if (mount(NULL, path, NULL, (orig_flags & ~flags_mask) | MS_REMOUNT | MS_BIND | new_flags, NULL) < 0) + return -errno; - r = mount(NULL, path, NULL, MS_REMOUNT | MS_BIND | MS_RDONLY | orig_flags, NULL); - - return r < 0 ? -errno : 0; + return 0; } static int make_read_only(const MountEntry *m, char **blacklist, FILE *proc_self_mountinfo) { + unsigned long new_flags = 0, flags_mask = 0; bool submounts = false; int r = 0; assert(m); assert(proc_self_mountinfo); - if (mount_entry_read_only(m)) { - if (IN_SET(m->mode, EMPTY_DIR, TMPFS)) { - 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) { - /* 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 + if (mount_entry_read_only(m) || m->mode == PRIVATE_DEV) { + new_flags |= MS_RDONLY; + flags_mask |= MS_RDONLY; + } + + if (m->nosuid) { + new_flags |= MS_NOSUID; + flags_mask |= MS_NOSUID; + } + + if (flags_mask == 0) /* No Change? */ return 0; - /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked read-only - * already stays this way. This improves compatibility with container managers, where we won't attempt to undo - * read-only mounts already applied. */ + /* We generally apply these changes recursively, except for /dev, and the cases we know there's + * nothing further down. 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. */ + submounts = + mount_entry_read_only(m) && + !IN_SET(m->mode, EMPTY_DIR, TMPFS); + if (submounts) + r = bind_remount_recursive_with_mountinfo(mount_entry_path(m), new_flags, flags_mask, blacklist, proc_self_mountinfo); + else + r = bind_remount_one(mount_entry_path(m), m->flags, new_flags, flags_mask); + + /* Not that we only turn on the MS_RDONLY flag here, we never turn it off. Something that was marked + * read-only already stays this way. This improves compatibility with container managers, where we + * won't attempt to undo read-only mounts already applied. */ if (r == -ENOENT && m->ignore) - r = 0; - + return 0; if (r < 0) - return log_debug_errno(r, "Failed to re-mount '%s'%s read-only: %m", mount_entry_path(m), + return log_debug_errno(r, "Failed to re-mount '%s'%s: %m", mount_entry_path(m), submounts ? " and its submounts" : ""); - return 0; } @@ -1117,6 +1153,7 @@ 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 + + (ns_info->protect_hostname ? 2 : 0) + (namespace_info_mount_apivfs(ns_info) ? ELEMENTSOF(apivfs_table) : 0); } @@ -1156,7 +1193,7 @@ int setup_namespace( _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; _cleanup_(dissected_image_unrefp) DissectedImage *dissected_image = NULL; _cleanup_free_ void *root_hash = NULL; - MountEntry *m, *mounts = NULL; + MountEntry *m = NULL, *mounts = NULL; size_t n_mounts, root_hash_size = 0; bool require_prefix = false; const char *root; @@ -1220,7 +1257,10 @@ int setup_namespace( protect_home, protect_system); if (n_mounts > 0) { - m = mounts = (MountEntry *) alloca0(n_mounts * sizeof(MountEntry)); + m = mounts = new0(MountEntry, n_mounts); + if (!mounts) + return -ENOMEM; + r = append_access_mounts(&m, read_write_paths, READWRITE, require_prefix); if (r < 0) goto finish; @@ -1265,6 +1305,7 @@ int setup_namespace( *(m++) = (MountEntry) { .path_const = "/dev", .mode = PRIVATE_DEV, + .flags = DEV_MOUNT_OPTIONS, }; } @@ -1301,6 +1342,17 @@ int setup_namespace( goto finish; } + if (ns_info->protect_hostname) { + *(m++) = (MountEntry) { + .path_const = "/proc/sys/kernel/hostname", + .mode = READONLY, + }; + *(m++) = (MountEntry) { + .path_const = "/proc/sys/kernel/domainname", + .mode = READONLY, + }; + } + assert(mounts + n_mounts == m); /* Prepend the root directory where that's necessary */ @@ -1380,7 +1432,7 @@ int setup_namespace( if (n_mounts > 0) { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; - char **blacklist; + _cleanup_free_ char **blacklist = NULL; size_t j; /* Open /proc/self/mountinfo now as it may become unavailable if we mount anything on top of /proc. @@ -1426,7 +1478,11 @@ int setup_namespace( } /* Create a blacklist we can pass to bind_mount_recursive() */ - blacklist = newa(char*, n_mounts+1); + blacklist = new(char*, n_mounts+1); + if (!blacklist) { + r = -ENOMEM; + goto finish; + } for (j = 0; j < n_mounts; j++) blacklist[j] = (char*) mount_entry_path(mounts+j); blacklist[j] = NULL; @@ -1460,6 +1516,8 @@ finish: for (m = mounts; m < mounts + n_mounts; m++) mount_entry_done(m); + free(mounts); + return r; } @@ -1502,6 +1560,7 @@ int bind_mount_add(BindMount **b, size_t *n, const BindMount *item) { .source = TAKE_PTR(s), .destination = TAKE_PTR(d), .read_only = item->read_only, + .nosuid = item->nosuid, .recursive = item->recursive, .ignore_enoent = item->ignore_enoent, }; @@ -1615,8 +1674,8 @@ int setup_tmp_dirs(const char *id, char **tmp_dir, char **var_tmp_dir) { char *t; t = strjoina(a, "/tmp"); - rmdir(t); - rmdir(a); + (void) rmdir(t); + (void) rmdir(a); free(a); return r; @@ -1649,14 +1708,14 @@ int setup_netns(int netns_storage_socket[static 2]) { netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT); if (netns == -EAGAIN) { - /* Nothing stored yet, so let's create a new namespace */ + /* Nothing stored yet, so let's create a new namespace. */ if (unshare(CLONE_NEWNET) < 0) { r = -errno; goto fail; } - loopback_setup(); + (void) loopback_setup(); netns = open("/proc/self/ns/net", O_RDONLY|O_CLOEXEC|O_NOCTTY); if (netns < 0) { @@ -1691,6 +1750,59 @@ fail: return r; } +int open_netns_path(int netns_storage_socket[static 2], const char *path) { + _cleanup_close_ int netns = -1; + int q, r; + + assert(netns_storage_socket); + assert(netns_storage_socket[0] >= 0); + assert(netns_storage_socket[1] >= 0); + assert(path); + + /* If the storage socket doesn't contain a netns fd yet, open one via the file system and store it in + * it. This is supposed to be called ahead of time, i.e. before setup_netns() which will allocate a + * new anonymous netns if needed. */ + + if (lockf(netns_storage_socket[0], F_LOCK, 0) < 0) + return -errno; + + netns = receive_one_fd(netns_storage_socket[0], MSG_DONTWAIT); + if (netns == -EAGAIN) { + /* Nothing stored yet. Open the file from the file system. */ + + netns = open(path, O_RDONLY|O_NOCTTY|O_CLOEXEC); + if (netns < 0) { + r = -errno; + goto fail; + } + + r = fd_is_network_ns(netns); + if (r == 0) { /* Not a netns? Refuse early. */ + r = -EINVAL; + goto fail; + } + if (r < 0 && r != -EUCLEAN) /* EUCLEAN: we don't know */ + goto fail; + + r = 1; + + } else if (netns < 0) { + r = netns; + goto fail; + } else + r = 0; /* Already allocated */ + + q = send_one_fd(netns_storage_socket[1], netns, MSG_DONTWAIT); + if (q < 0) { + r = q; + goto fail; + } + +fail: + (void) lockf(netns_storage_socket[0], F_ULOCK, 0); + return r; +} + bool ns_type_supported(NamespaceType type) { const char *t, *ns_proc; diff --git a/src/core/namespace.h b/src/core/namespace.h index 5e0ec9796..022bdb614 100644 --- a/src/core/namespace.h +++ b/src/core/namespace.h @@ -52,12 +52,14 @@ struct NamespaceInfo { bool protect_kernel_tunables:1; bool protect_kernel_modules:1; bool mount_apivfs:1; + bool protect_hostname:1; }; struct BindMount { char *source; char *destination; bool read_only:1; + bool nosuid:1; bool recursive:1; bool ignore_enoent:1; }; @@ -92,6 +94,7 @@ int setup_tmp_dirs( char **var_tmp_dir); int setup_netns(int netns_storage_socket[static 2]); +int open_netns_path(int netns_storage_socket[static 2], const char *path); const char* protect_home_to_string(ProtectHome p) _const_; ProtectHome protect_home_from_string(const char *s) _pure_; diff --git a/src/core/path.c b/src/core/path.c index 831e49df2..5a00150e9 100644 --- a/src/core/path.c +++ b/src/core/path.c @@ -474,7 +474,7 @@ static void path_enter_running(Path *p) { return; } - r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(p)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); if (r < 0) goto fail; @@ -555,19 +555,16 @@ static void path_mkdir(Path *p) { static int path_start(Unit *u) { Path *p = PATH(u); - Unit *trigger; int r; assert(p); assert(IN_SET(p->state, PATH_DEAD, PATH_FAILED)); - trigger = UNIT_TRIGGER(u); - if (!trigger || trigger->load_state != UNIT_LOADED) { - log_unit_error(u, "Refusing to start, unit to trigger not loaded."); - return -ENOENT; - } + r = unit_test_trigger_loaded(u); + if (r < 0) + return r; - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { path_enter_dead(p, PATH_FAILURE_START_LIMIT_HIT); return r; diff --git a/src/core/scope.c b/src/core/scope.c index e478661f9..7f8305225 100644 --- a/src/core/scope.c +++ b/src/core/scope.c @@ -333,11 +333,11 @@ static int scope_start(Unit *u) { (void) unit_reset_cpu_accounting(u); (void) unit_reset_ip_accounting(u); - unit_export_state_files(UNIT(s)); + unit_export_state_files(u); - r = unit_attach_pids_to_cgroup(u, UNIT(s)->pids, NULL); + r = unit_attach_pids_to_cgroup(u, u->pids, NULL); if (r < 0) { - log_unit_warning_errno(UNIT(s), r, "Failed to add PIDs to scope's control group: %m"); + log_unit_warning_errno(u, r, "Failed to add PIDs to scope's control group: %m"); scope_enter_dead(s, SCOPE_FAILURE_RESOURCES); return r; } @@ -347,7 +347,7 @@ static int scope_start(Unit *u) { scope_set_state(s, SCOPE_RUNNING); /* Start watching the PIDs currently in the scope */ - (void) unit_enqueue_rewatch_pids(UNIT(s)); + (void) unit_enqueue_rewatch_pids(u); return 1; } diff --git a/src/core/selinux-access.c b/src/core/selinux-access.c index 0c6d885b8..c8b1a8588 100644 --- a/src/core/selinux-access.c +++ b/src/core/selinux-access.c @@ -17,6 +17,7 @@ #include "alloc-util.h" #include "audit-fd.h" #include "bus-util.h" +#include "format-util.h" #include "log.h" #include "path-util.h" #include "selinux-util.h" @@ -109,7 +110,11 @@ _printf_(2, 3) static int log_callback(int type, const char *fmt, ...) { va_end(ap); if (r >= 0) { - audit_log_user_avc_message(fd, AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); + if (type == SELINUX_AVC) + audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_AVC, buf, NULL, NULL, NULL, 0); + else if (type == SELINUX_ERROR) + audit_log_user_avc_message(get_audit_fd(), AUDIT_USER_SELINUX_ERR, buf, NULL, NULL, NULL, 0); + return 0; } } diff --git a/src/core/selinux-setup.c b/src/core/selinux-setup.c index bac1aa3ff..b8a94a52a 100644 --- a/src/core/selinux-setup.c +++ b/src/core/selinux-setup.c @@ -13,6 +13,7 @@ #include "selinux-setup.h" #include "selinux-util.h" #include "string-util.h" +#include "time-util.h" #include "util.h" #if HAVE_SELINUX diff --git a/src/core/service.c b/src/core/service.c index 324dcf231..02899901f 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "sd-messages.h" @@ -143,8 +145,6 @@ static void service_unwatch_pid_file(Service *s) { } static int service_set_main_pid(Service *s, pid_t pid) { - pid_t ppid; - assert(s); if (pid <= 1) @@ -163,12 +163,10 @@ static int service_set_main_pid(Service *s, pid_t pid) { s->main_pid = pid; s->main_pid_known = true; + s->main_pid_alien = pid_is_my_child(pid) == 0; - if (get_process_ppid(pid, &ppid) >= 0 && ppid != getpid_cached()) { + if (s->main_pid_alien) log_unit_warning(UNIT(s), "Supervising process "PID_FMT" which is not our child. We'll most likely not notice when it exits.", pid); - s->main_pid_alien = true; - } else - s->main_pid_alien = false; return 0; } @@ -994,7 +992,7 @@ static int service_load_pid_file(Service *s, bool may_warn) { if (r < 0) return r; - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, false); if (r < 0) /* FIXME: we need to do something here */ return log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" for service: %m", pid); @@ -1024,7 +1022,7 @@ static void service_search_main_pid(Service *s) { if (service_set_main_pid(s, pid) < 0) return; - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, false); if (r < 0) /* FIXME: we need to do something here */ log_unit_warning_errno(UNIT(s), r, "Failed to watch PID "PID_FMT" from: %m", pid); @@ -1158,7 +1156,7 @@ static int service_coldplug(Unit *u) { SERVICE_RUNNING, SERVICE_RELOAD, 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); + r = unit_watch_pid(UNIT(s), s->main_pid, false); if (r < 0) return r; } @@ -1170,7 +1168,7 @@ static int service_coldplug(Unit *u) { SERVICE_RELOAD, 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); + r = unit_watch_pid(UNIT(s), s->control_pid, false); if (r < 0) return r; } @@ -1458,7 +1456,7 @@ static int service_spawn( if (r < 0) return r; - our_env = new0(char*, 9); + our_env = new0(char*, 10); if (!our_env) return -ENOMEM; @@ -1474,6 +1472,10 @@ static int service_spawn( if (asprintf(our_env + n_env++, "MANAGERPID="PID_FMT, getpid_cached()) < 0) return -ENOMEM; + if (s->pid_file) + if (asprintf(our_env + n_env++, "PIDFILE=%s", s->pid_file) < 0) + return -ENOMEM; + if (s->socket_fd >= 0) { union sockaddr_union sa; socklen_t salen = sizeof(sa); @@ -1566,8 +1568,8 @@ static int service_spawn( 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 */ + r = unit_watch_pid(UNIT(s), pid, true); + if (r < 0) return r; *_pid = pid; @@ -1754,6 +1756,9 @@ static void service_enter_dead(Service *s, ServiceResult f, bool allow_restart) if (s->pid_file) (void) unlink(s->pid_file); + /* Reset TTY ownership if necessary */ + exec_context_revert_tty(&s->exec_context); + return; fail: @@ -2006,6 +2011,26 @@ static void service_kill_control_process(Service *s) { } } +static int service_adverse_to_leftover_processes(Service *s) { + assert(s); + + /* KillMode=mixed and control group are used to indicate that all process should be killed off. + * SendSIGKILL is used for services that require a clean shutdown. These are typically database + * service where a SigKilled process would result in a lengthy recovery and who's shutdown or + * startup time is quite variable (so Timeout settings aren't of use). + * + * Here we take these two factors and refuse to start a service if there are existing processes + * within a control group. Databases, while generally having some protection against multiple + * instances running, lets not stress the rigor of these. Also ExecStartPre parts of the service + * aren't as rigoriously written to protect aganst against multiple use. */ + if (unit_warn_leftover_processes(UNIT(s)) && + IN_SET(s->kill_context.kill_mode, KILL_MIXED, KILL_CONTROL_GROUP) && + !s->kill_context.send_sigkill) { + return log_unit_error_errno(UNIT(s), SYNTHETIC_ERRNO(EBUSY), "Will not start SendSIGKILL=no service of type KillMode=control-group or mixed while processes exist"); + } + return 0; +} + static void service_enter_start(Service *s) { ExecCommand *c; usec_t timeout; @@ -2017,7 +2042,9 @@ static void service_enter_start(Service *s) { service_unwatch_control_pid(s); service_unwatch_main_pid(s); - unit_warn_leftover_processes(UNIT(s)); + r = service_adverse_to_leftover_processes(s); + if (r < 0) + goto fail; if (s->type == SERVICE_FORKING) { s->control_command_id = SERVICE_EXEC_START; @@ -2110,7 +2137,9 @@ static void service_enter_start_pre(Service *s) { s->control_command = s->exec_command[SERVICE_EXEC_START_PRE]; if (s->control_command) { - unit_warn_leftover_processes(UNIT(s)); + r = service_adverse_to_leftover_processes(s); + if (r < 0) + goto fail; s->control_command_id = SERVICE_EXEC_START_PRE; @@ -2154,7 +2183,7 @@ static void service_enter_restart(Service *s) { * restarted. We use JOB_RESTART (instead of the more obvious * JOB_START) here so that those dependency jobs will be added * as well. */ - r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_RESTART, UNIT(s), JOB_REPLACE, NULL, &error, NULL); if (r < 0) goto fail; @@ -2336,7 +2365,7 @@ static int service_start(Unit *u) { assert(IN_SET(s->state, SERVICE_DEAD, SERVICE_FAILED)); /* Make sure we don't enter a busy loop of some kind. */ - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { service_enter_dead(s, SERVICE_FAILURE_START_LIMIT_HIT, false); return r; @@ -3186,11 +3215,19 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { bool notify_dbus = true; Service *s = SERVICE(u); ServiceResult f; + ExitClean clean_mode; assert(s); assert(pid >= 0); - if (is_clean_exit(code, status, s->type == SERVICE_ONESHOT ? EXIT_CLEAN_COMMAND : EXIT_CLEAN_DAEMON, &s->success_status)) + /* Oneshot services and non-SERVICE_EXEC_START commands should not be + * considered daemons as they are typically not long running. */ + if (s->type == SERVICE_ONESHOT || (s->control_pid == pid && s->control_command_id != SERVICE_EXEC_START)) + clean_mode = EXIT_CLEAN_COMMAND; + else + clean_mode = EXIT_CLEAN_DAEMON; + + if (is_clean_exit(code, status, clean_mode, &s->success_status)) f = SERVICE_SUCCESS; else if (code == CLD_EXITED) f = SERVICE_FAILURE_EXIT_CODE; @@ -3439,8 +3476,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (main_pid_good(s) <= 0) service_enter_stop_post(s, f); - /* If there is still a service - * process around, wait until + /* If there is still a service process around, wait until * that one quit, too */ break; @@ -3462,10 +3498,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { if (notify_dbus) unit_add_to_dbus_queue(u); - /* If we get a SIGCHLD event for one of the processes we were interested in, then we look for others to watch, - * under the assumption that we'll sooner or later get a SIGCHLD for them, as the original process we watched - * was probably the parent of them, and they are hence now our children. */ - (void) unit_enqueue_rewatch_pids(u); + /* We watch the main/control process otherwise we can't retrieve the unit they + * belong to with cgroupv1. But if they are not our direct child, we won't get a + * SIGCHLD for them. Therefore we need to look for others to watch so we can + * detect when the cgroup becomes empty. Note that the control process is always + * our child so it's pointless to watch all other processes. */ + if (!control_pid_good(s)) + if (!s->main_pid_known || s->main_pid_alien) + (void) unit_enqueue_rewatch_pids(u); } static int service_dispatch_timer(sd_event_source *source, usec_t usec, void *userdata) { @@ -3673,7 +3713,7 @@ static void service_notify_message( if (r > 0) { service_set_main_pid(s, new_main_pid); - r = unit_watch_pid(UNIT(s), new_main_pid); + r = unit_watch_pid(UNIT(s), new_main_pid, false); if (r < 0) log_unit_warning_errno(UNIT(s), r, "Failed to watch new main PID "PID_FMT" for service: %m", new_main_pid); @@ -3891,7 +3931,7 @@ static void service_bus_name_owner_change( log_unit_debug(u, "D-Bus name %s is now owned by process " PID_FMT, name, pid); service_set_main_pid(s, pid); - unit_watch_pid(UNIT(s), pid); + unit_watch_pid(UNIT(s), pid, false); } } } diff --git a/src/core/service.h b/src/core/service.h index 9c4340c70..57eefbb34 100644 --- a/src/core/service.h +++ b/src/core/service.h @@ -152,6 +152,7 @@ struct Service { /* Keep restart intention between UNIT_FAILED and UNIT_ACTIVATING */ bool will_auto_restart:1; bool start_timeout_defined:1; + bool exec_fd_hot:1; char *bus_name; char *bus_name_owner; /* unique name of the current owner */ @@ -183,7 +184,6 @@ struct Service { unsigned n_restarts; bool flush_n_restarts; - bool exec_fd_hot; }; extern const UnitVTable service_vtable; diff --git a/src/core/show-status.c b/src/core/show-status.c index f748a8208..02601e52d 100644 --- a/src/core/show-status.c +++ b/src/core/show-status.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "alloc-util.h" #include "fd-util.h" #include "io-util.h" diff --git a/src/core/smack-setup.c b/src/core/smack-setup.c index 49b37aefc..cd7fb0141 100644 --- a/src/core/smack-setup.c +++ b/src/core/smack-setup.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "alloc-util.h" #include "dirent-util.h" diff --git a/src/core/socket.c b/src/core/socket.c index af95e9027..836e513d4 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -1473,6 +1473,25 @@ static int socket_address_listen_do( log_unit_error_errno(u, error, fmt, strna(_t)); \ }) +static int fork_needed(const SocketAddress *address, const ExecContext *context) { + int r; + + assert(address); + assert(context); + + /* Check if we need to do the cgroup or netns stuff. If not we can do things much simpler. */ + + if (IN_SET(address->sockaddr.sa.sa_family, AF_INET, AF_INET6)) { + r = bpf_firewall_supported(); + if (r < 0) + return r; + if (r != BPF_FIREWALL_UNSUPPORTED) /* If BPF firewalling isn't supported anyway — there's no point in this forking complexity */ + return true; + } + + return context->private_network || context->network_namespace_path; +} + static int socket_address_listen_in_cgroup( Socket *s, const SocketAddress *address, @@ -1485,18 +1504,34 @@ static int socket_address_listen_in_cgroup( assert(s); assert(address); - /* This is a wrapper around socket_address_listen(), that forks off a helper process inside the socket's cgroup - * in which the socket is actually created. This way we ensure the socket is actually properly attached to the - * unit's cgroup for the purpose of BPF filtering and such. */ + /* This is a wrapper around socket_address_listen(), that forks off a helper process inside the + * socket's cgroup and network namespace in which the socket is actually created. This way we ensure + * the socket is actually properly attached to the unit's cgroup for the purpose of BPF filtering and + * such. */ - if (!IN_SET(address->sockaddr.sa.sa_family, AF_INET, AF_INET6)) - goto shortcut; /* BPF filtering only applies to IPv4 + IPv6, shortcut things for other protocols */ - - r = bpf_firewall_supported(); + r = fork_needed(address, &s->exec_context); if (r < 0) return r; - if (r == BPF_FIREWALL_UNSUPPORTED) /* If BPF firewalling isn't supported anyway — there's no point in this forking complexity */ - goto shortcut; + if (r == 0) { + /* Shortcut things... */ + fd = socket_address_listen_do(s, address, label); + if (fd < 0) + return log_address_error_errno(UNIT(s), address, fd, "Failed to create listening socket (%s): %m"); + + return fd; + } + + r = unit_setup_exec_runtime(UNIT(s)); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed acquire runtime: %m"); + + if (s->exec_context.network_namespace_path && + s->exec_runtime && + s->exec_runtime->netns_storage_socket[0] >= 0) { + r = open_netns_path(s->exec_runtime->netns_storage_socket, s->exec_context.network_namespace_path); + if (r < 0) + return log_unit_error_errno(UNIT(s), r, "Failed to open network namespace path %s: %m", s->exec_context.network_namespace_path); + } if (socketpair(AF_UNIX, SOCK_SEQPACKET|SOCK_CLOEXEC, 0, pair) < 0) return log_unit_error_errno(UNIT(s), errno, "Failed to create communication channel: %m"); @@ -1509,6 +1544,23 @@ static int socket_address_listen_in_cgroup( pair[0] = safe_close(pair[0]); + if ((s->exec_context.private_network || s->exec_context.network_namespace_path) && + s->exec_runtime && + s->exec_runtime->netns_storage_socket[0] >= 0) { + + if (ns_type_supported(NAMESPACE_NET)) { + r = setup_netns(s->exec_runtime->netns_storage_socket); + if (r < 0) { + log_unit_error_errno(UNIT(s), r, "Failed to join network namespace: %m"); + _exit(EXIT_NETWORK); + } + } else if (s->exec_context.network_namespace_path) { + log_unit_error(UNIT(s), "Network namespace path configured but network namespaces not supported."); + _exit(EXIT_NETWORK); + } else + log_unit_warning(UNIT(s), "PrivateNetwork=yes is configured, but the kernel does not support network namespaces, ignoring."); + } + fd = socket_address_listen_do(s, address, label); if (fd < 0) { log_address_error_errno(UNIT(s), address, fd, "Failed to create listening socket (%s): %m"); @@ -1538,13 +1590,6 @@ static int socket_address_listen_in_cgroup( return log_address_error_errno(UNIT(s), address, fd, "Failed to receive listening socket (%s): %m"); return fd; - -shortcut: - fd = socket_address_listen_do(s, address, label); - if (fd < 0) - return log_address_error_errno(UNIT(s), address, fd, "Failed to create listening socket (%s): %m"); - - return fd; } DEFINE_TRIVIAL_CLEANUP_FUNC(Socket *, socket_close_fds); @@ -1807,7 +1852,7 @@ static int socket_coldplug(Unit *u) { SOCKET_FINAL_SIGTERM, SOCKET_FINAL_SIGKILL)) { - r = unit_watch_pid(UNIT(s), s->control_pid); + r = unit_watch_pid(UNIT(s), s->control_pid, false); if (r < 0) return r; @@ -1893,9 +1938,8 @@ static int socket_spawn(Socket *s, ExecCommand *c, pid_t *_pid) { if (r < 0) return r; - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, true); if (r < 0) - /* FIXME: we need to do something here */ return r; *_pid = pid; @@ -1964,7 +2008,7 @@ static int socket_chown(Socket *s, pid_t *_pid) { _exit(EXIT_SUCCESS); } - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, true); if (r < 0) goto fail; @@ -2266,7 +2310,7 @@ static void socket_enter_running(Socket *s, int cfd) { goto fail; } - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT_DEREF(s->service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) goto fail; } @@ -2341,7 +2385,7 @@ static void socket_enter_running(Socket *s, int cfd) { service->peer = TAKE_PTR(p); /* Pass ownership of the peer reference */ - r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(s)->manager, JOB_START, UNIT(service), JOB_REPLACE, NULL, &error, NULL); if (r < 0) { /* We failed to activate the new service, but it still exists. Let's make sure the service * closes and forgets the connection fd again, immediately. */ @@ -2442,7 +2486,7 @@ static int socket_start(Unit *u) { assert(IN_SET(s->state, SOCKET_DEAD, SOCKET_FAILED)); - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { socket_enter_dead(s, SOCKET_FAILURE_START_LIMIT_HIT); return r; @@ -2812,17 +2856,10 @@ static int socket_accept_do(Socket *s, int fd) { assert(s); assert(fd >= 0); - for (;;) { - cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK); - if (cfd < 0) { - if (errno == EINTR) - continue; - - return -errno; - } - - break; - } + cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); + if (cfd < 0) + /* Convert transient network errors into clean and well-defined EAGAIN */ + return ERRNO_IS_ACCEPT_AGAIN(errno) ? -EAGAIN : -errno; return cfd; } @@ -2860,6 +2897,8 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { pair[0] = safe_close(pair[0]); cfd = socket_accept_do(s, fd); + if (cfd == -EAGAIN) /* spurious accept() */ + _exit(EXIT_SUCCESS); if (cfd < 0) { log_unit_error_errno(UNIT(s), cfd, "Failed to accept connection socket: %m"); _exit(EXIT_FAILURE); @@ -2884,6 +2923,10 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { return r; } + /* If we received no fd, we got EIO here. If this happens with a process exit code of EXIT_SUCCESS + * this is a spurious accept(), let's convert that back to EAGAIN here. */ + if (cfd == -EIO) + return -EAGAIN; if (cfd < 0) return log_unit_error_errno(UNIT(s), cfd, "Failed to receive connection socket: %m"); @@ -2891,6 +2934,8 @@ static int socket_accept_in_cgroup(Socket *s, SocketPort *p, int fd) { shortcut: cfd = socket_accept_do(s, fd); + if (cfd == -EAGAIN) /* spurious accept(), skip it silently */ + return -EAGAIN; if (cfd < 0) return log_unit_error_errno(UNIT(s), cfd, "Failed to accept connection socket: %m"); @@ -2910,7 +2955,6 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, log_unit_debug(UNIT(p->socket), "Incoming traffic"); if (revents != EPOLLIN) { - if (revents & EPOLLHUP) log_unit_error(UNIT(p->socket), "Got POLLHUP on a listening socket. The service probably invoked shutdown() on it, and should better not do that."); else @@ -2923,6 +2967,8 @@ static int socket_dispatch_io(sd_event_source *source, int fd, uint32_t revents, socket_address_can_accept(&p->address)) { cfd = socket_accept_in_cgroup(p->socket, p, fd); + if (cfd == -EAGAIN) /* Spurious accept() */ + return 0; if (cfd < 0) goto fail; diff --git a/src/core/swap.c b/src/core/swap.c index 2d8463b8b..28acef237 100644 --- a/src/core/swap.c +++ b/src/core/swap.c @@ -550,7 +550,7 @@ static int swap_coldplug(Unit *u) { pid_is_unwaited(s->control_pid) && SWAP_STATE_WITH_PROCESS(new_state)) { - r = unit_watch_pid(UNIT(s), s->control_pid); + r = unit_watch_pid(UNIT(s), s->control_pid, false); if (r < 0) return r; @@ -657,9 +657,8 @@ static int swap_spawn(Swap *s, ExecCommand *c, pid_t *_pid) { if (r < 0) goto fail; - r = unit_watch_pid(UNIT(s), pid); + r = unit_watch_pid(UNIT(s), pid, true); if (r < 0) - /* FIXME: we need to do something here */ goto fail; *_pid = pid; @@ -871,7 +870,7 @@ static int swap_start(Unit *u) { if (UNIT(other)->job && UNIT(other)->job->state == JOB_RUNNING) return -EAGAIN; - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { swap_enter_dead(s, SWAP_FAILURE_START_LIMIT_HIT); return r; diff --git a/src/core/systemd.pc.in b/src/core/systemd.pc.in index 0dae95047..5d1ddd762 100644 --- a/src/core/systemd.pc.in +++ b/src/core/systemd.pc.in @@ -8,25 +8,27 @@ # (at your option) any later version. prefix=@prefix@ -systemdutildir=@rootlibexecdir@ -systemdsystemunitdir=@systemunitdir@ -systemdsystempresetdir=@systempresetdir@ -systemduserunitdir=@userunitdir@ -systemduserpresetdir=@userpresetdir@ -systemdsystemconfdir=@pkgsysconfdir@/system -systemduserconfdir=@pkgsysconfdir@/user +rootprefix=@rootprefix_noslash@ +sysconfdir=@sysconfdir@ +systemdutildir=${rootprefix}/lib/systemd +systemdsystemunitdir=${rootprefix}/lib/systemd/system +systemdsystempresetdir=${rootprefix}/lib/systemd/system-preset +systemduserunitdir=${prefix}/lib/systemd/user +systemduserpresetdir=${prefix}/lib/systemd/user-preset +systemdsystemconfdir=${sysconfdir}/systemd/system +systemduserconfdir=${sysconfdir}/systemd/user systemdsystemunitpath=${systemdsystemconfdir}:/etc/systemd/system:/run/systemd/system:/usr/local/lib/systemd/system:${systemdsystemunitdir}:/usr/lib/systemd/system:/lib/systemd/system systemduserunitpath=${systemduserconfdir}:/etc/systemd/user:/run/systemd/user:/usr/local/lib/systemd/user:/usr/local/share/systemd/user:${systemduserunitdir}:/usr/lib/systemd/user:/usr/share/systemd/user -systemdsystemgeneratordir=@systemgeneratordir@ -systemdusergeneratordir=@usergeneratordir@ -systemdsleepdir=@systemsleepdir@ -systemdshutdowndir=@systemshutdowndir@ -tmpfilesdir=@tmpfilesdir@ -sysusersdir=@sysusersdir@ -sysctldir=@sysctldir@ -binfmtdir=@binfmtdir@ -modulesloaddir=@modulesloaddir@ -catalogdir=@catalogdir@ +systemdsystemgeneratordir=${rootprefix}/lib/systemd/system-generators +systemdusergeneratordir=${prefix}/lib/systemd/user-generators +systemdsleepdir=${rootprefix}/lib/systemd/system-sleep +systemdshutdowndir=${rootprefix}/lib/systemd/system-shutdown +tmpfilesdir=${prefix}/lib/tmpfiles.d +sysusersdir=${prefix}/lib/sysusers.d +sysctldir=${prefix}/lib/sysctl.d +binfmtdir=${prefix}/lib/binfmt.d +modulesloaddir=${prefix}/lib/modules-load.d +catalogdir=${prefix}/lib/systemd/catalog systemuidmax=@systemuidmax@ systemgidmax=@systemgidmax@ dynamicuidmin=@dynamicuidmin@ diff --git a/src/core/timer.c b/src/core/timer.c index d9ba2f76b..8440bb27b 100644 --- a/src/core/timer.c +++ b/src/core/timer.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include #include "alloc-util.h" @@ -73,7 +77,7 @@ static int timer_verify(Timer *t) { if (UNIT(t)->load_state != UNIT_LOADED) return 0; - if (!t->values) { + if (!t->values && !t->on_clock_change && !t->on_timezone_change) { log_unit_error(UNIT(t), "Timer unit lacks value setting. Refusing."); return -ENOEXEC; } @@ -211,14 +215,18 @@ static void timer_dump(Unit *u, FILE *f, const char *prefix) { "%sPersistent: %s\n" "%sWakeSystem: %s\n" "%sAccuracy: %s\n" - "%sRemainAfterElapse: %s\n", + "%sRemainAfterElapse: %s\n" + "%sOnClockChange: %s\n" + "%sOnTimeZoneChange %s\n", prefix, timer_state_to_string(t->state), prefix, timer_result_to_string(t->result), prefix, trigger ? trigger->id : "n/a", prefix, yes_no(t->persistent), prefix, yes_no(t->wake_system), prefix, format_timespan(buf, sizeof(buf), t->accuracy_usec, 1), - prefix, yes_no(t->remain_after_elapse)); + prefix, yes_no(t->remain_after_elapse), + prefix, yes_no(t->on_clock_change), + prefix, yes_no(t->on_timezone_change)); LIST_FOREACH(value, v, t->values) { @@ -380,6 +388,13 @@ static void timer_enter_waiting(Timer *t, bool time_change) { if (r < 0) continue; + /* To make the delay due to RandomizedDelaySec= work even at boot, + * if the scheduled time has already passed, set the time when systemd + * first started as the scheduled time. + * Also, we don't have to check t->persistent since the logic implicitly express true. */ + if (v->next_elapse < UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime) + v->next_elapse = UNIT(t)->manager->timestamps[MANAGER_TIMESTAMP_USERSPACE].realtime; + if (!found_realtime) t->next_elapse_realtime = v->next_elapse; else @@ -463,7 +478,7 @@ static void timer_enter_waiting(Timer *t, bool time_change) { } } - if (!found_monotonic && !found_realtime) { + if (!found_monotonic && !found_realtime && !t->on_timezone_change && !t->on_clock_change) { log_unit_debug(UNIT(t), "Timer is elapsed."); timer_enter_elapsed(t, leave_around); return; @@ -568,7 +583,7 @@ static void timer_enter_running(Timer *t) { return; } - r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, &error, NULL); + r = manager_add_job(UNIT(t)->manager, JOB_START, trigger, JOB_REPLACE, NULL, &error, NULL); if (r < 0) goto fail; @@ -588,19 +603,16 @@ fail: static int timer_start(Unit *u) { Timer *t = TIMER(u); TimerValue *v; - Unit *trigger; int r; assert(t); assert(IN_SET(t->state, TIMER_DEAD, TIMER_FAILED)); - trigger = UNIT_TRIGGER(u); - if (!trigger || trigger->load_state != UNIT_LOADED) { - log_unit_error(u, "Refusing to start, unit to trigger not loaded."); - return -ENOENT; - } + r = unit_test_trigger_loaded(u); + if (r < 0) + return r; - r = unit_start_limit_test(u); + r = unit_test_start_limit(u); if (r < 0) { timer_enter_dead(t, TIMER_FAILURE_START_LIMIT_HIT); return r; @@ -807,8 +819,13 @@ static void timer_time_change(Unit *u) { if (t->last_trigger.realtime > ts) t->last_trigger.realtime = ts; - log_unit_debug(u, "Time change, recalculating next elapse."); - timer_enter_waiting(t, true); + if (t->on_clock_change) { + log_unit_debug(u, "Time change, triggering activation."); + timer_enter_running(t); + } else { + log_unit_debug(u, "Time change, recalculating next elapse."); + timer_enter_waiting(t, true); + } } static void timer_timezone_change(Unit *u) { @@ -819,8 +836,13 @@ static void timer_timezone_change(Unit *u) { if (t->state != TIMER_WAITING) return; - log_unit_debug(u, "Timezone change, recalculating next elapse."); - timer_enter_waiting(t, false); + if (t->on_timezone_change) { + log_unit_debug(u, "Timezone change, triggering activation."); + timer_enter_running(t); + } else { + log_unit_debug(u, "Timezone change, recalculating next elapse."); + timer_enter_waiting(t, false); + } } static const char* const timer_base_table[_TIMER_BASE_MAX] = { diff --git a/src/core/timer.h b/src/core/timer.h index 833aadb0b..ab66a201a 100644 --- a/src/core/timer.h +++ b/src/core/timer.h @@ -57,6 +57,8 @@ struct Timer { bool persistent; bool wake_system; bool remain_after_elapse; + bool on_clock_change; + bool on_timezone_change; char *stamp_path; }; diff --git a/src/core/transaction.c b/src/core/transaction.c index 2418332b9..3b6b240d3 100644 --- a/src/core/transaction.c +++ b/src/core/transaction.c @@ -279,32 +279,40 @@ static int transaction_merge_jobs(Transaction *tr, sd_bus_error *e) { } static void transaction_drop_redundant(Transaction *tr) { - Job *j; - Iterator i; + bool again; - /* Goes through the transaction and removes all jobs of the units - * whose jobs are all noops. If not all of a unit's jobs are - * redundant, they are kept. */ + /* Goes through the transaction and removes all jobs of the units whose jobs are all noops. If not + * all of a unit's jobs are redundant, they are kept. */ assert(tr); -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - Job *k; + do { + Iterator i; + Job *j; - LIST_FOREACH(transaction, k, j) { + again = false; - if (tr->anchor_job == k || - !job_type_is_redundant(k->type, unit_active_state(k->unit)) || - (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) - goto next_unit; + HASHMAP_FOREACH(j, tr->jobs, i) { + bool keep = false; + Job *k; + + LIST_FOREACH(transaction, k, j) + if (tr->anchor_job == k || + !job_type_is_redundant(k->type, unit_active_state(k->unit)) || + (k->unit->job && job_type_is_conflicting(k->type, k->unit->job->type))) { + keep = true; + break; + } + + if (!keep) { + log_trace("Found redundant job %s/%s, dropping from transaction.", + j->unit->id, job_type_to_string(j->type)); + transaction_delete_job(tr, j, false); + again = true; + break; + } } - - log_trace("Found redundant job %s/%s, dropping from transaction.", j->unit->id, job_type_to_string(j->type)); - transaction_delete_job(tr, j, false); - goto rescan; - next_unit:; - } + } while (again); } _pure_ static bool unit_matters_to_anchor(Unit *u, Job *j) { @@ -485,29 +493,36 @@ static int transaction_verify_order(Transaction *tr, unsigned *generation, sd_bu } static void transaction_collect_garbage(Transaction *tr) { - Iterator i; - Job *j; + bool again; assert(tr); /* Drop jobs that are not required by any other job */ -rescan: - HASHMAP_FOREACH(j, tr->jobs, i) { - if (tr->anchor_job == j) - continue; - if (j->object_list) { + do { + Iterator i; + Job *j; + + again = false; + + HASHMAP_FOREACH(j, tr->jobs, i) { + if (tr->anchor_job == j) + continue; + + if (!j->object_list) { + log_trace("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); + transaction_delete_job(tr, j, true); + again = true; + break; + } + log_trace("Keeping job %s/%s because of %s/%s", j->unit->id, job_type_to_string(j->type), j->object_list->subject ? j->object_list->subject->unit->id : "root", j->object_list->subject ? job_type_to_string(j->object_list->subject->type) : "root"); - continue; } - log_trace("Garbage collecting job %s/%s", j->unit->id, job_type_to_string(j->type)); - transaction_delete_job(tr, j, true); - goto rescan; - } + } while (again); } static int transaction_is_destructive(Transaction *tr, JobMode mode, sd_bus_error *e) { @@ -589,7 +604,12 @@ rescan: } } -static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { +static int transaction_apply( + Transaction *tr, + Manager *m, + JobMode mode, + Set *affected_jobs) { + Iterator i; Job *j; int r; @@ -646,6 +666,11 @@ static int transaction_apply(Transaction *tr, Manager *m, JobMode mode) { job_add_to_dbus_queue(j); job_start_timer(j, false); job_shutdown_magic(j); + + /* When 'affected' is specified, let's track all in it all jobs that were touched because of + * this transaction. */ + if (affected_jobs) + (void) set_put(affected_jobs, j); } return 0; @@ -658,7 +683,13 @@ rollback: return r; } -int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e) { +int transaction_activate( + Transaction *tr, + Manager *m, + JobMode mode, + Set *affected_jobs, + sd_bus_error *e) { + Iterator i; Job *j; int r; @@ -735,7 +766,7 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error 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); + r = transaction_apply(tr, m, mode, affected_jobs); if (r < 0) return log_warning_errno(r, "Failed to apply transaction: %m"); diff --git a/src/core/transaction.h b/src/core/transaction.h index 70d74a4cc..4b5620f5c 100644 --- a/src/core/transaction.h +++ b/src/core/transaction.h @@ -29,6 +29,6 @@ int transaction_add_job_and_dependencies( bool ignore_requirements, bool ignore_order, sd_bus_error *e); -int transaction_activate(Transaction *tr, Manager *m, JobMode mode, sd_bus_error *e); +int transaction_activate(Transaction *tr, Manager *m, JobMode mode, Set *affected, sd_bus_error *e); int transaction_add_isolate_jobs(Transaction *tr, Manager *m); void transaction_abort(Transaction *tr); diff --git a/src/core/unit.c b/src/core/unit.c index 24b14fbcd..2cde494a7 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -1589,66 +1589,43 @@ fail: return log_unit_debug_errno(u, r, "Failed to load configuration: %m"); } -static bool unit_condition_test_list(Unit *u, Condition *first, const char *(*to_string)(ConditionType t)) { - Condition *c; - int triggered = -1; +_printf_(7, 8) +static int log_unit_internal(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) { + Unit *u = userdata; + va_list ap; + int r; - assert(u); - assert(to_string); + va_start(ap, format); + if (u) + r = log_object_internalv(level, error, file, line, func, + u->manager->unit_log_field, + u->id, + u->manager->invocation_log_field, + u->invocation_id_string, + format, ap); + else + r = log_internalv(level, error, file, line, func, format, ap); + va_end(ap); - /* If the condition list is empty, then it is true */ - if (!first) - return true; - - /* Otherwise, if all of the non-trigger conditions apply and - * if any of the trigger conditions apply (unless there are - * none) we return true */ - LIST_FOREACH(conditions, c, first) { - int r; - - r = condition_test(c); - if (r < 0) - log_unit_warning(u, - "Couldn't determine result for %s=%s%s%s, assuming failed: %m", - to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter); - else - log_unit_debug(u, - "%s=%s%s%s %s.", - to_string(c->type), - c->trigger ? "|" : "", - c->negate ? "!" : "", - c->parameter, - condition_result_to_string(c->result)); - - if (!c->trigger && r <= 0) - return false; - - if (c->trigger && triggered <= 0) - triggered = r > 0; - } - - return triggered != 0; + return r; } -static bool unit_condition_test(Unit *u) { +static bool unit_test_condition(Unit *u) { assert(u); dual_timestamp_get(&u->condition_timestamp); - u->condition_result = unit_condition_test_list(u, u->conditions, condition_type_to_string); + u->condition_result = condition_test_list(u->conditions, condition_type_to_string, log_unit_internal, u); unit_add_to_dbus_queue(u); return u->condition_result; } -static bool unit_assert_test(Unit *u) { +static bool unit_test_assert(Unit *u) { assert(u); dual_timestamp_get(&u->assert_timestamp); - u->assert_result = unit_condition_test_list(u, u->asserts, assert_type_to_string); + u->assert_result = condition_test_list(u->asserts, assert_type_to_string, log_unit_internal, u); unit_add_to_dbus_queue(u); @@ -1667,7 +1644,7 @@ void unit_status_printf(Unit *u, const char *status, const char *unit_status_msg REENABLE_WARNING; } -int unit_start_limit_test(Unit *u) { +int unit_test_start_limit(Unit *u) { const char *reason; assert(u); @@ -1682,9 +1659,11 @@ int unit_start_limit_test(Unit *u) { 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); + emergency_action(u->manager, u->start_limit_action, + EMERGENCY_ACTION_IS_WATCHDOG|EMERGENCY_ACTION_WARN, + u->reboot_arg, -1, reason); + + return -ECANCELED; } bool unit_shall_confirm_spawn(Unit *u) { @@ -1725,27 +1704,31 @@ static bool unit_verify_deps(Unit *u) { return true; } -/* Errors: - * -EBADR: This unit type does not support starting. +/* Errors that aren't really errors: * -EALREADY: Unit is already started. + * -ECOMM: Condition failed * -EAGAIN: An operation is already in progress. Retry later. - * -ECANCELED: Too many requests for now. + * + * Errors that are real errors: + * -EBADR: This unit type does not support starting. + * -ECANCELED: Start limit hit, too many requests for now * -EPROTO: Assert failed * -EINVAL: Unit not loaded * -EOPNOTSUPP: Unit type not supported * -ENOLINK: The necessary dependencies are not fulfilled. * -ESTALE: This unit has been started before and can't be started a second time + * -ENOENT: This is a triggering unit and unit to trigger is not loaded */ int unit_start(Unit *u) { UnitActiveState state; Unit *following; + int r; assert(u); - /* If this is already started, then this will succeed. Note - * that this will even succeed if this unit is not startable - * by the user. This is relied on to detect when we need to - * wait for units and when waiting is finished. */ + /* If this is already started, then this will succeed. Note that this will even succeed if this unit + * is not startable by the user. This is relied on to detect when we need to wait for units and when + * waiting is finished. */ state = unit_active_state(u); if (UNIT_IS_ACTIVE_OR_RELOADING(state)) return -EALREADY; @@ -1758,35 +1741,45 @@ int unit_start(Unit *u) { if (UNIT_VTABLE(u)->once_only && dual_timestamp_is_set(&u->inactive_enter_timestamp)) return -ESTALE; - /* If the conditions failed, don't do anything at all. If we - * already are activating this call might still be useful to - * speed up activation in case there is some hold-off time, - * but we don't want to recheck the condition in that case. */ + /* If the conditions failed, don't do anything at all. If we already are activating this call might + * still be useful to speed up activation in case there is some hold-off time, but we don't want to + * recheck the condition in that case. */ if (state != UNIT_ACTIVATING && - !unit_condition_test(u)) { - log_unit_debug(u, "Starting requested but condition failed. Not starting unit."); - return -ECOMM; + !unit_test_condition(u)) { + + /* Let's also check the start limit here. Normally, the start limit is only checked by the + * .start() method of the unit type after it did some additional checks verifying everything + * is in order (so that those other checks can propagate errors properly). However, if a + * condition check doesn't hold we don't get that far but we should still ensure we are not + * called in a tight loop without a rate limit check enforced, hence do the check here. Note + * that ECOMM is generally not a reason for a job to fail, unlike most other errors here, + * hence the chance is big that any triggering unit for us will trigger us again. Note this + * condition check is a bit different from the condition check inside the per-unit .start() + * function, as this one will not change the unit's state in any way (and we shouldn't here, + * after all the condition failed). */ + + r = unit_test_start_limit(u); + if (r < 0) + return r; + + return log_unit_debug_errno(u, SYNTHETIC_ERRNO(ECOMM), "Starting requested but condition failed. Not starting unit."); } /* If the asserts failed, fail the entire job */ if (state != UNIT_ACTIVATING && - !unit_assert_test(u)) { - log_unit_notice(u, "Starting requested but asserts failed."); - return -EPROTO; - } + !unit_test_assert(u)) + return log_unit_notice_errno(u, SYNTHETIC_ERRNO(EPROTO), "Starting requested but asserts failed."); - /* Units of types that aren't supported cannot be - * started. Note that we do this test only after the condition - * checks, so that we rather return condition check errors - * (which are usually not considered a true failure) than "not - * supported" errors (which are considered a failure). + /* Units of types that aren't supported cannot be started. Note that we do this test only after the + * condition checks, so that we rather return condition check errors (which are usually not + * considered a true failure) than "not supported" errors (which are considered a failure). */ if (!unit_supported(u)) return -EOPNOTSUPP; - /* Let's make sure that the deps really are in order before we start this. Normally the job engine should have - * taken care of this already, but let's check this here again. After all, our dependencies might not be in - * effect anymore, due to a reload or due to a failed condition. */ + /* Let's make sure that the deps really are in order before we start this. Normally the job engine + * should have taken care of this already, but let's check this here again. After all, our + * dependencies might not be in effect anymore, due to a reload or due to a failed condition. */ if (!unit_verify_deps(u)) return -ENOLINK; @@ -1801,11 +1794,9 @@ int unit_start(Unit *u) { if (!UNIT_VTABLE(u)->start) return -EBADR; - /* We don't suppress calls to ->start() here when we are - * already starting, to allow this request to be used as a - * "hurry up" call, for example when the unit is in some "auto - * restart" state where it waits for a holdoff timer to elapse - * before it will start again. */ + /* We don't suppress calls to ->start() here when we are already starting, to allow this request to + * be used as a "hurry up" call, for example when the unit is in some "auto restart" state where it + * waits for a holdoff timer to elapse before it will start again. */ unit_add_to_dbus_queue(u); @@ -1895,7 +1886,7 @@ int unit_reload(Unit *u) { state = unit_active_state(u); if (state == UNIT_RELOADING) - return -EALREADY; + return -EAGAIN; if (state != UNIT_ACTIVE) { log_unit_warning(u, "Unit cannot be reloaded because it is inactive."); @@ -2044,7 +2035,7 @@ static void unit_check_binds_to(Unit *u) { log_unit_info(u, "Unit is bound to inactive unit %s. Stopping, too.", other->id); /* A unit we need to run is gone. Sniff. Let's stop this. */ - r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, &error, NULL); + r = manager_add_job(u->manager, JOB_STOP, u, JOB_FAIL, NULL, &error, NULL); if (r < 0) log_unit_warning_errno(u, r, "Failed to enqueue stop job, ignoring: %s", bus_error_message(&error, r)); } @@ -2060,25 +2051,25 @@ static void retroactively_start_dependencies(Unit *u) { HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_REQUIRES], i) if (!hashmap_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL); HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BINDS_TO], i) if (!hashmap_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_REPLACE, NULL, NULL, NULL); HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_WANTS], i) if (!hashmap_get(u->dependencies[UNIT_AFTER], other) && !UNIT_IS_ACTIVE_OR_ACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL); + manager_add_job(u->manager, JOB_START, other, JOB_FAIL, NULL, NULL, NULL); HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTS], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL); HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_CONFLICTED_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL); } static void retroactively_stop_dependencies(Unit *u) { @@ -2092,7 +2083,7 @@ static void retroactively_stop_dependencies(Unit *u) { /* Pull down units which are bound to us recursively if enabled */ HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_BOUND_BY], i) if (!UNIT_IS_INACTIVE_OR_DEACTIVATING(unit_active_state(other))) - manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL); + manager_add_job(u->manager, JOB_STOP, other, JOB_REPLACE, NULL, NULL, NULL); } void unit_start_on_failure(Unit *u) { @@ -2111,7 +2102,7 @@ void unit_start_on_failure(Unit *u) { HASHMAP_FOREACH_KEY(v, other, u->dependencies[UNIT_ON_FAILURE], i) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, &error, NULL); + r = manager_add_job(u->manager, JOB_START, other, u->on_failure_job_mode, NULL, &error, NULL); if (r < 0) log_unit_warning_errno(u, r, "Failed to enqueue OnFailure= job, ignoring: %s", bus_error_message(&error, r)); } @@ -2501,17 +2492,17 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlag 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); + 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); + emergency_action(m, u->success_action, 0, u->reboot_arg, unit_success_action_exit_status(u), reason); } } unit_add_to_gc_queue(u); } -int unit_watch_pid(Unit *u, pid_t pid) { +int unit_watch_pid(Unit *u, pid_t pid, bool exclusive) { int r; assert(u); @@ -2519,6 +2510,12 @@ int unit_watch_pid(Unit *u, pid_t pid) { /* Watch a specific PID */ + /* Caller might be sure that this PID belongs to this unit only. Let's take this + * opportunity to remove any stalled references to this PID as they can be created + * easily (when watching a process which is not our direct child). */ + if (exclusive) + manager_unwatch_pid(u->manager, pid); + r = set_ensure_allocated(&u->pids, NULL); if (r < 0) return r; @@ -3198,7 +3195,7 @@ static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) { return serialize_item(f, key, s); } -static const char *ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = { +static const char *const ip_accounting_metric_field[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = { [CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes", [CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets", [CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes", @@ -4091,14 +4088,20 @@ int unit_patch_contexts(Unit *u) { return -ENOMEM; } - /* If the dynamic user option is on, let's make sure that the unit can't leave its UID/GID - * around in the file system or on IPC objects. Hence enforce a strict sandbox. */ + /* If the dynamic user option is on, let's make sure that the unit can't leave its + * UID/GID around in the file system or on IPC objects. Hence enforce a strict + * sandbox. */ ec->private_tmp = true; ec->remove_ipc = true; ec->protect_system = PROTECT_SYSTEM_STRICT; if (ec->protect_home == PROTECT_HOME_NO) ec->protect_home = PROTECT_HOME_READ_ONLY; + + /* Make sure this service can neither benefit from SUID/SGID binaries nor create + * them. */ + ec->no_new_privileges = true; + ec->restrict_suid_sgid = true; } } @@ -4428,7 +4431,7 @@ int unit_make_transient(Unit *u) { return 0; } -static void log_kill(pid_t pid, int sig, void *userdata) { +static int log_kill(pid_t pid, int sig, void *userdata) { _cleanup_free_ char *comm = NULL; (void) get_process_comm(pid, &comm); @@ -4436,13 +4439,15 @@ static void log_kill(pid_t pid, int sig, void *userdata) { /* Don't log about processes marked with brackets, under the assumption that these are temporary processes only, like for example systemd's own PAM stub process. */ if (comm && comm[0] == '(') - return; + return 0; log_unit_notice(userdata, "Killing process " PID_FMT " (%s) with signal SIG%s.", pid, strna(comm), signal_to_string(sig)); + + return 1; } static int operation_to_signal(KillContext *c, KillOperation k) { @@ -4607,7 +4612,7 @@ int unit_require_mounts_for(Unit *u, const char *path, UnitDependencyMask mask) if (!p) return -ENOMEM; - path = path_simplify(p, false); + path = path_simplify(p, true); if (!path_is_normalized(path)) return -EPERM; @@ -5394,29 +5399,31 @@ int unit_prepare_exec(Unit *u) { return 0; } -static void log_leftover(pid_t pid, int sig, void *userdata) { +static int log_leftover(pid_t pid, int sig, void *userdata) { _cleanup_free_ char *comm = NULL; (void) get_process_comm(pid, &comm); if (comm && comm[0] == '(') /* Most likely our own helper process (PAM?), ignore */ - return; + return 0; log_unit_warning(userdata, "Found left-over process " PID_FMT " (%s) in control group while starting unit. Ignoring.\n" "This usually indicates unclean termination of a previous run, or service implementation deficiencies.", pid, strna(comm)); + + return 1; } -void unit_warn_leftover_processes(Unit *u) { +int unit_warn_leftover_processes(Unit *u) { assert(u); (void) unit_pick_cgroup_path(u); if (!u->cgroup_path) - return; + return 0; - (void) cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u); + return cg_kill_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, 0, 0, NULL, log_leftover, u); } bool unit_needs_console(Unit *u) { @@ -5585,6 +5592,20 @@ int unit_success_action_exit_status(Unit *u) { return r; } +int unit_test_trigger_loaded(Unit *u) { + Unit *trigger; + + /* Tests whether the unit to trigger is loaded */ + + trigger = UNIT_TRIGGER(u); + if (!trigger) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit to trigger not loaded."); + if (trigger->load_state != UNIT_LOADED) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(ENOENT), "Refusing to start, unit %s to trigger not loaded.", u->id); + + return 0; +} + 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 43cf15715..28878b5f4 100644 --- a/src/core/unit.h +++ b/src/core/unit.h @@ -349,6 +349,9 @@ typedef struct Unit { bool exported_log_rate_limit_interval:1; bool exported_log_rate_limit_burst:1; + /* Whether we warned about clamping the CPU quota period */ + bool warned_clamping_cpu_quota_period: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. */ signed int last_section_private:2; @@ -675,7 +678,7 @@ typedef enum UnitNotifyFlags { void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, UnitNotifyFlags flags); -int unit_watch_pid(Unit *u, pid_t pid); +int unit_watch_pid(Unit *u, pid_t pid, bool exclusive); void unit_unwatch_pid(Unit *u, pid_t pid); void unit_unwatch_all_pids(Unit *u); @@ -775,7 +778,7 @@ static inline bool unit_supported(Unit *u) { void unit_warn_if_dir_nonempty(Unit *u, const char* where); int unit_fail_if_noncanonical(Unit *u, const char* where); -int unit_start_limit_test(Unit *u); +int unit_test_start_limit(Unit *u); void unit_unref_uid(Unit *u, bool destroy_now); int unit_ref_uid(Unit *u, uid_t uid, bool clean_ipc); @@ -804,7 +807,7 @@ void unit_unlink_state_files(Unit *u); int unit_prepare_exec(Unit *u); -void unit_warn_leftover_processes(Unit *u); +int unit_warn_leftover_processes(Unit *u); bool unit_needs_console(Unit *u); @@ -827,6 +830,8 @@ int unit_exit_status(Unit *u); int unit_success_action_exit_status(Unit *u); int unit_failure_action_exit_status(Unit *u); +int unit_test_trigger_loaded(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-vacuum.c b/src/coredump/coredump-vacuum.c index 6ce5dfc71..35885dfb4 100644 --- a/src/coredump/coredump-vacuum.c +++ b/src/coredump/coredump-vacuum.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include +#include +#include #include "alloc-util.h" #include "coredump-vacuum.h" @@ -9,10 +12,10 @@ #include "fs-util.h" #include "hashmap.h" #include "macro.h" +#include "memory-util.h" #include "string-util.h" #include "time-util.h" #include "user-util.h" -#include "util.h" #define DEFAULT_MAX_USE_LOWER (uint64_t) (1ULL*1024ULL*1024ULL) /* 1 MiB */ #define DEFAULT_MAX_USE_UPPER (uint64_t) (4ULL*1024ULL*1024ULL*1024ULL) /* 4 GiB */ diff --git a/src/coredump/coredump.c b/src/coredump/coredump.c index ecbb4bffc..023701646 100644 --- a/src/coredump/coredump.c +++ b/src/coredump/coredump.c @@ -35,6 +35,7 @@ #include "log.h" #include "macro.h" #include "main-func.h" +#include "memory-util.h" #include "missing.h" #include "mkdir.h" #include "parse-util.h" @@ -48,7 +49,6 @@ #include "strv.h" #include "tmpfile-util.h" #include "user-util.h" -#include "util.h" /* The maximum size up to which we process coredumps */ #define PROCESS_SIZE_MAX ((uint64_t) (2LLU*1024LLU*1024LLU*1024LLU)) @@ -369,7 +369,7 @@ static int save_external_coredump( if (r < 0) return log_error_errno(r, "Failed to determine coredump file name: %m"); - mkdir_p_label("/var/lib/systemd/coredump", 0755); + (void) mkdir_p_label("/var/lib/systemd/coredump", 0755); fd = open_tmpfile_linkable(fn, O_RDWR|O_CLOEXEC, &tmp); if (fd < 0) diff --git a/src/coredump/coredumpctl.c b/src/coredump/coredumpctl.c index fbee24296..b239d8194 100644 --- a/src/coredump/coredumpctl.c +++ b/src/coredump/coredumpctl.c @@ -859,7 +859,7 @@ static int save_core(sd_journal *j, FILE *file, char **path, bool *unlink_temp) error: if (temp) { - unlink(temp); + (void) unlink(temp); log_debug("Removed temporary file %s", temp); } return r; @@ -985,7 +985,7 @@ finish: if (unlink_path) { log_debug("Removed temporary file %s", path); - unlink(path); + (void) unlink(path); } return r; diff --git a/src/coredump/stacktrace.c b/src/coredump/stacktrace.c index dab4c1ad9..ab1ac12a2 100644 --- a/src/coredump/stacktrace.c +++ b/src/coredump/stacktrace.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "alloc-util.h" #include "fd-util.h" diff --git a/src/cryptsetup/cryptsetup-generator.c b/src/cryptsetup/cryptsetup-generator.c index 8759a2614..e9b21689c 100644 --- a/src/cryptsetup/cryptsetup-generator.c +++ b/src/cryptsetup/cryptsetup-generator.c @@ -1,7 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include +#include +#include #include "alloc-util.h" #include "dropin.h" @@ -262,6 +265,7 @@ static int create_disk( "RemainAfterExit=yes\n" "TimeoutSec=0\n" /* the binary handles timeouts anyway */ "KeyringMode=shared\n" /* make sure we can share cached keys among instances */ + "OOMScoreAdjust=500\n" /* unlocking can allocate a lot of memory if Argon2 is used */ "ExecStart=" SYSTEMD_CRYPTSETUP_PATH " attach '%s' '%s' '%s' '%s'\n" "ExecStop=" SYSTEMD_CRYPTSETUP_PATH " detach '%s'\n", name_escaped, u_escaped, strempty(password_escaped), strempty(filtered_escaped), @@ -287,10 +291,6 @@ static int create_disk( return log_error_errno(r, "Failed to write unit file %s: %m", n); if (!noauto) { - r = generator_add_symlink(arg_dest, d, "wants", n); - if (r < 0) - return r; - r = generator_add_symlink(arg_dest, netdev ? "remote-cryptsetup.target" : "cryptsetup.target", nofail ? "wants" : "requires", n); diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c index 9cb52ddf2..864d6ff47 100644 --- a/src/cryptsetup/cryptsetup.c +++ b/src/cryptsetup/cryptsetup.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "sd-device.h" @@ -16,12 +19,12 @@ #include "log.h" #include "main-func.h" #include "mount-util.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" +#include "pretty-print.h" #include "string-util.h" #include "strv.h" -#include "pretty-print.h" -#include "util.h" /* internal helper */ #define ANY_LUKS "LUKS" @@ -44,6 +47,8 @@ static unsigned arg_tries = 3; static bool arg_readonly = false; static bool arg_verify = false; static bool arg_discards = false; +static bool arg_same_cpu_crypt = false; +static bool arg_submit_from_crypt_cpus = false; static bool arg_tcrypt_hidden = false; static bool arg_tcrypt_system = false; #ifdef CRYPT_TCRYPT_VERA_MODES @@ -199,6 +204,10 @@ static int parse_one_option(const char *option) { arg_verify = true; else if (STR_IN_SET(option, "allow-discards", "discard")) arg_discards = true; + else if (streq(option, "same-cpu-crypt")) + arg_same_cpu_crypt = true; + else if (streq(option, "submit-from-crypt-cpus")) + arg_submit_from_crypt_cpus = true; else if (streq(option, "luks")) arg_type = ANY_LUKS; else if (streq(option, "tcrypt")) @@ -557,6 +566,10 @@ static int attach_luks_or_plain(struct crypt_device *cd, log_error_errno(r, "Failed to activate with key file '%s'. (Key data incorrect?)", key_file); return -EAGAIN; /* Log actual error, but return EAGAIN */ } + if (r == -EINVAL) { + log_error_errno(r, "Failed to activate with key file '%s'. (Key file missing?)", key_file); + return -EAGAIN; /* Log actual error, but return EAGAIN */ + } if (r < 0) return log_error_errno(r, "Failed to activate with key file '%s': %m", key_file); } else { @@ -602,6 +615,24 @@ static int help(void) { return 0; } +static uint32_t determine_flags(void) { + uint32_t flags = 0; + + if (arg_readonly) + flags |= CRYPT_ACTIVATE_READONLY; + + if (arg_discards) + flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; + + if (arg_same_cpu_crypt) + flags |= CRYPT_ACTIVATE_SAME_CPU_CRYPT; + + if (arg_submit_from_crypt_cpus) + flags |= CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS; + + return flags; +} + static int run(int argc, char *argv[]) { _cleanup_(crypt_freep) struct crypt_device *cd = NULL; int r; @@ -666,11 +697,7 @@ static int run(int argc, char *argv[]) { return 0; } - if (arg_readonly) - flags |= CRYPT_ACTIVATE_READONLY; - - if (arg_discards) - flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS; + flags = determine_flags(); if (arg_timeout == USEC_INFINITY) until = 0; diff --git a/src/debug-generator/debug-generator.c b/src/debug-generator/debug-generator.c index fa4ca57bb..1b5fb2a28 100644 --- a/src/debug-generator/debug-generator.c +++ b/src/debug-generator/debug-generator.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "generator.h" #include "mkdir.h" @@ -9,7 +11,6 @@ #include "string-util.h" #include "strv.h" #include "unit-name.h" -#include "util.h" static const char *arg_dest = NULL; static char *arg_default_unit = NULL; diff --git a/src/delta/delta.c b/src/delta/delta.c index 1ffbc6c57..c28a816d6 100644 --- a/src/delta/delta.c +++ b/src/delta/delta.c @@ -14,6 +14,7 @@ #include "locale-util.h" #include "log.h" #include "main-func.h" +#include "nulstr-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -24,7 +25,6 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" -#include "util.h" static const char prefixes[] = "/etc\0" diff --git a/src/environment-d-generator/environment-d-generator.c b/src/environment-d-generator/environment-d-generator.c index 9d64d9573..b2558f0c8 100644 --- a/src/environment-d-generator/environment-d-generator.c +++ b/src/environment-d-generator/environment-d-generator.c @@ -14,7 +14,7 @@ static int environment_dirs(char ***ret) { _cleanup_free_ char *c = NULL; int r; - dirs = strv_split_nulstr(CONF_PATHS_NULSTR("environment.d")); + dirs = strv_new(CONF_PATHS_USR("environment.d"), NULL); if (!dirs) return -ENOMEM; diff --git a/src/firstboot/firstboot.c b/src/firstboot/firstboot.c index dde11576e..c21cf76b9 100644 --- a/src/firstboot/firstboot.c +++ b/src/firstboot/firstboot.c @@ -27,6 +27,7 @@ #include "fileio.h" #include "fs-util.h" #include "hostname-util.h" +#include "kbd-util.h" #include "locale-util.h" #include "main-func.h" #include "mkdir.h" @@ -253,7 +254,7 @@ static int process_locale(void) { if (arg_copy_locale && arg_root) { mkdir_parents(etc_localeconf, 0755); - r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, COPY_REFLINK); + r = copy_file("/etc/locale.conf", etc_localeconf, 0, 0644, 0, 0, COPY_REFLINK); if (r != -ENOENT) { if (r < 0) return log_error_errno(r, "Failed to copy %s: %m", etc_localeconf); @@ -327,7 +328,7 @@ static int process_keymap(void) { if (arg_copy_keymap && arg_root) { mkdir_parents(etc_vconsoleconf, 0755); - r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, 0, COPY_REFLINK); + r = copy_file("/etc/vconsole.conf", etc_vconsoleconf, 0, 0644, 0, 0, COPY_REFLINK); if (r != -ENOENT) { if (r < 0) return log_error_errno(r, "Failed to copy %s: %m", etc_vconsoleconf); diff --git a/src/fsck/fsck.c b/src/fsck/fsck.c index 524327c4d..8101f9ce9 100644 --- a/src/fsck/fsck.c +++ b/src/fsck/fsck.c @@ -265,6 +265,7 @@ static int fsck_progress_socket(void) { static int run(int argc, char *argv[]) { _cleanup_close_pair_ int progress_pipe[2] = { -1, -1 }; _cleanup_(sd_device_unrefp) sd_device *dev = NULL; + _cleanup_free_ char *dpath = NULL; const char *device, *type; bool root_directory; struct stat st; @@ -290,7 +291,11 @@ static int run(int argc, char *argv[]) { return 0; if (argc > 1) { - device = argv[1]; + dpath = strdup(argv[1]); + if (!dpath) + return log_oom(); + + device = dpath; if (stat(device, &st) < 0) return log_error_errno(errno, "Failed to stat %s: %m", device); diff --git a/src/fstab-generator/fstab-generator.c b/src/fstab-generator/fstab-generator.c index 55a8242fc..d35f2f993 100644 --- a/src/fstab-generator/fstab-generator.c +++ b/src/fstab-generator/fstab-generator.c @@ -119,8 +119,7 @@ static int add_swap( if (r < 0) return r; - fputs("# Automatically generated by systemd-fstab-generator\n\n" - "[Unit]\n" + fputs("[Unit]\n" "SourcePath=/etc/fstab\n" "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n\n" "[Swap]\n", f); @@ -354,6 +353,12 @@ static int add_mount( "Documentation=man:fstab(5) man:systemd-fstab-generator(8)\n", source); + /* All mounts under /sysroot need to happen later, at initrd-fs.target time. IOW, it's not + * technically part of the basic initrd filesystem itself, and so shouldn't inherit the default + * Before=local-fs.target dependency. */ + if (in_initrd() && path_startswith(where, "/sysroot")) + fprintf(f, "DefaultDependencies=no\n"); + if (STRPTR_IN_SET(fstype, "nfs", "nfs4") && !(flags & AUTOMOUNT) && fstab_test_yes_no_option(opts, "bg\0" "fg\0")) { /* The default retry timeout that mount.nfs uses for 'bg' mounts @@ -518,6 +523,8 @@ static int parse_fstab(bool initrd) { int r = 0; fstab_path = initrd ? "/sysroot/etc/fstab" : "/etc/fstab"; + log_debug("Parsing %s...", fstab_path); + f = setmntent(fstab_path, "re"); if (!f) { if (errno == ENOENT) @@ -571,9 +578,9 @@ static int parse_fstab(bool initrd) { noauto = fstab_test_yes_no_option(me->mnt_opts, "noauto\0" "auto\0"); nofail = fstab_test_yes_no_option(me->mnt_opts, "nofail\0" "fail\0"); - log_debug("Found entry what=%s where=%s type=%s makefs=%s nofail=%s noauto=%s", + log_debug("Found entry what=%s where=%s type=%s makefs=%s growfs=%s noauto=%s nofail=%s", what, where, me->mnt_type, - yes_no(makefs), + yes_no(makefs), yes_no(growfs), yes_no(noauto), yes_no(nofail)); if (streq(me->mnt_type, "swap")) @@ -720,23 +727,15 @@ static int add_sysroot_usr_mount(void) { } static int add_volatile_root(void) { - const char *from, *to; - - if (arg_volatile_mode != VOLATILE_YES) - return 0; /* Let's add in systemd-remount-volatile.service which will remount the root device to tmpfs if this is - * requested, leaving only /usr from the root mount inside. */ + * requested (or as an overlayfs), leaving only /usr from the root mount inside. */ - from = strjoina(SYSTEM_DATA_UNIT_PATH "/systemd-volatile-root.service"); - to = strjoina(arg_dest, "/" SPECIAL_INITRD_ROOT_FS_TARGET, ".requires/systemd-volatile-root.service"); + if (!IN_SET(arg_volatile_mode, VOLATILE_YES, VOLATILE_OVERLAY)) + return 0; - (void) mkdir_parents(to, 0755); - - if (symlink(from, to) < 0) - return log_error_errno(errno, "Failed to hook in volatile remount service: %m"); - - return 0; + return generator_add_symlink(arg_dest, SPECIAL_INITRD_ROOT_FS_TARGET, "requires", + SYSTEM_DATA_UNIT_PATH "/" SPECIAL_VOLATILE_ROOT_SERVICE); } static int add_volatile_var(void) { @@ -868,7 +867,7 @@ static int determine_root(void) { } static int run(const char *dest, const char *dest_early, const char *dest_late) { - int r; + int r, r2 = 0, r3 = 0; assert_se(arg_dest = dest); assert_se(arg_dest_late = dest_late); @@ -881,42 +880,27 @@ static int run(const char *dest, const char *dest_early, const char *dest_late) /* Always honour root= and usr= in the kernel command line if we are in an initrd */ if (in_initrd()) { - int k; - r = add_sysroot_mount(); - k = add_sysroot_usr_mount(); - if (k < 0) - r = k; + r2 = add_sysroot_usr_mount(); - k = add_volatile_root(); - if (k < 0) - r = k; + r3 = add_volatile_root(); } else r = add_volatile_var(); /* Honour /etc/fstab only when that's enabled */ if (arg_fstab_enabled) { - int k; - - log_debug("Parsing /etc/fstab"); - /* Parse the local /etc/fstab, possibly from the initrd */ - k = parse_fstab(false); - if (k < 0) - r = k; + r2 = parse_fstab(false); /* If running in the initrd also parse the /etc/fstab from the host */ - if (in_initrd()) { - log_debug("Parsing /sysroot/etc/fstab"); - - k = parse_fstab(true); - if (k < 0) - r = k; - } + if (in_initrd()) + r3 = parse_fstab(true); + else + r3 = generator_enable_remount_fs_service(arg_dest); } - return r; + return r < 0 ? r : r2 < 0 ? r2 : r3; } DEFINE_MAIN_GENERATOR_FUNCTION(run); diff --git a/src/fuzz/fuzz-bus-label.c b/src/fuzz/fuzz-bus-label.c new file mode 100644 index 000000000..46a3d23dc --- /dev/null +++ b/src/fuzz/fuzz-bus-label.c @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "bus-label.h" +#include "fuzz.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_free_ char *unescaped = NULL, *escaped = NULL; + + unescaped = bus_label_unescape_n((const char*)data, size); + assert_se(unescaped != NULL); + escaped = bus_label_escape(unescaped); + assert_se(escaped != NULL); + + return 0; +} diff --git a/src/fuzz/fuzz-calendarspec.c b/src/fuzz/fuzz-calendarspec.c new file mode 100644 index 000000000..8f031503e --- /dev/null +++ b/src/fuzz/fuzz-calendarspec.c @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "calendarspec.h" +#include "fd-util.h" +#include "fuzz.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(calendar_spec_freep) CalendarSpec *cspec = NULL; + _cleanup_free_ char *str = NULL, *p = NULL; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + str = memdup_suffix0(data, size); + + if (calendar_spec_from_string(str, &cspec) >= 0) { + (void) calendar_spec_valid(cspec); + (void) calendar_spec_normalize(cspec); + (void) calendar_spec_to_string(cspec, &p); + } + + return 0; +} diff --git a/src/fuzz/fuzz-dhcp-server.c b/src/fuzz/fuzz-dhcp-server.c index 01fe350ca..71bb99a38 100644 --- a/src/fuzz/fuzz-dhcp-server.c +++ b/src/fuzz/fuzz-dhcp-server.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "fuzz.h" #include "sd-dhcp-server.c" diff --git a/src/fuzz/fuzz-dhcp6-client.c b/src/fuzz/fuzz-dhcp6-client.c index c9bc2b381..6d887eaf1 100644 --- a/src/fuzz/fuzz-dhcp6-client.c +++ b/src/fuzz/fuzz-dhcp6-client.c @@ -49,6 +49,9 @@ static void fuzz_client(const uint8_t *data, size_t size, bool is_information_re } int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + if (size > 65536) + return 0; + /* This triggers client_receive_advertise */ fuzz_client(data, size, false); diff --git a/src/fuzz/fuzz-dhcp6-client.options b/src/fuzz/fuzz-dhcp6-client.options new file mode 100644 index 000000000..678d526b1 --- /dev/null +++ b/src/fuzz/fuzz-dhcp6-client.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65536 diff --git a/src/fuzz/fuzz-dns-packet.c b/src/fuzz/fuzz-dns-packet.c index c150c8184..9bf95b9c7 100644 --- a/src/fuzz/fuzz-dns-packet.c +++ b/src/fuzz/fuzz-dns-packet.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "fuzz.h" +#include "memory-util.h" #include "resolved-dns-packet.h" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { diff --git a/src/fuzz/fuzz-env-file.c b/src/fuzz/fuzz-env-file.c new file mode 100644 index 000000000..3c8ffaa7b --- /dev/null +++ b/src/fuzz/fuzz-env-file.c @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "env-file.h" +#include "fd-util.h" +#include "fuzz.h" +#include "strv.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_strv_free_ char **rl = NULL, **rlp = NULL; + + if (size == 0 || size > 65535) + return 0; + + f = fmemopen((char*) data, size, "re"); + assert_se(f); + + /* 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); + + (void) load_env_file(f, NULL, &rl); + assert_se(fseek(f, 0, SEEK_SET) == 0); + (void) load_env_file_pairs(f, NULL, &rlp); + + return 0; +} diff --git a/src/fuzz/fuzz-env-file.options b/src/fuzz/fuzz-env-file.options new file mode 100644 index 000000000..0824b19fa --- /dev/null +++ b/src/fuzz/fuzz-env-file.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/src/fuzz/fuzz-hostname-util.c b/src/fuzz/fuzz-hostname-util.c new file mode 100644 index 000000000..deaf8112b --- /dev/null +++ b/src/fuzz/fuzz-hostname-util.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "hostname-util.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_free_ char *ret = NULL; + + if (size == 0) + return 0; + + f = fmemopen((char*) data, size, "re"); + assert_se(f); + + /* 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); + + (void) read_etc_hostname_stream(f, &ret); + + return 0; +} diff --git a/src/fuzz/fuzz-journald-stream.c b/src/fuzz/fuzz-journald-stream.c index 5d6c8eb8c..0991b287c 100644 --- a/src/fuzz/fuzz-journald-stream.c +++ b/src/fuzz/fuzz-journald-stream.c @@ -2,6 +2,7 @@ #include #include +#include #include "fd-util.h" #include "fuzz.h" @@ -15,7 +16,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { StdoutStream *stream; int v; - if (size == 0) + if (size == 0 || size > 65536) return 0; if (!getenv("SYSTEMD_LOG_LEVEL")) diff --git a/src/fuzz/fuzz-journald-stream.options b/src/fuzz/fuzz-journald-stream.options new file mode 100644 index 000000000..678d526b1 --- /dev/null +++ b/src/fuzz/fuzz-journald-stream.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65536 diff --git a/src/fuzz/fuzz-lldp.c b/src/fuzz/fuzz-lldp.c index b9291d478..7f252309c 100644 --- a/src/fuzz/fuzz-lldp.c +++ b/src/fuzz/fuzz-lldp.c @@ -23,6 +23,9 @@ 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; + if (size > 2048) + return 0; + assert_se(sd_event_new(&e) == 0); assert_se(sd_lldp_new(&lldp) >= 0); assert_se(sd_lldp_set_ifindex(lldp, 42) >= 0); diff --git a/src/fuzz/fuzz-lldp.options b/src/fuzz/fuzz-lldp.options new file mode 100644 index 000000000..60bd9b0b2 --- /dev/null +++ b/src/fuzz/fuzz-lldp.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 2048 diff --git a/src/fuzz/fuzz-ndisc-rs.c b/src/fuzz/fuzz-ndisc-rs.c index 3a1e60fc5..d8d256d12 100644 --- a/src/fuzz/fuzz-ndisc-rs.c +++ b/src/fuzz/fuzz-ndisc-rs.c @@ -43,6 +43,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; _cleanup_(sd_ndisc_unrefp) sd_ndisc *nd = NULL; + if (size > 2048) + return 0; + assert_se(sd_event_new(&e) >= 0); assert_se(sd_ndisc_new(&nd) >= 0); assert_se(sd_ndisc_attach_event(nd, e, 0) >= 0); diff --git a/src/fuzz/fuzz-ndisc-rs.options b/src/fuzz/fuzz-ndisc-rs.options new file mode 100644 index 000000000..60bd9b0b2 --- /dev/null +++ b/src/fuzz/fuzz-ndisc-rs.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 2048 diff --git a/src/fuzz/fuzz-nspawn-oci.c b/src/fuzz/fuzz-nspawn-oci.c new file mode 100644 index 000000000..f7b59f13b --- /dev/null +++ b/src/fuzz/fuzz-nspawn-oci.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "nspawn-oci.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_(settings_freep) Settings *s = NULL; + + if (size == 0) + return 0; + + f = fmemopen((char*) data, size, "re"); + assert_se(f); + + /* 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); + + (void) oci_load(f, "/dev/null", &s); + + return 0; +} diff --git a/src/fuzz/fuzz-nspawn-settings.c b/src/fuzz/fuzz-nspawn-settings.c new file mode 100644 index 000000000..6c81eb773 --- /dev/null +++ b/src/fuzz/fuzz-nspawn-settings.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "alloc-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "nspawn-settings.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_fclose_ FILE *f = NULL; + _cleanup_(settings_freep) Settings *s = NULL; + + if (size == 0) + return 0; + + f = fmemopen((char*) data, size, "re"); + assert_se(f); + + /* 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); + + (void) settings_load(f, "/dev/null", &s); + + return 0; +} diff --git a/src/fuzz/fuzz-time-util.c b/src/fuzz/fuzz-time-util.c new file mode 100644 index 000000000..22b2496fc --- /dev/null +++ b/src/fuzz/fuzz-time-util.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "fd-util.h" +#include "fuzz.h" +#include "time-util.h" +#include "util.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_free_ char *str = NULL; + usec_t usec; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + str = memdup_suffix0(data, size); + + (void) parse_timestamp(str, &usec); + (void) parse_sec(str, &usec); + (void) parse_sec_fix_0(str, &usec); + (void) parse_sec_def_infinity(str, &usec); + (void) parse_time(str, &usec, USEC_PER_SEC); + (void) parse_nsec(str, &usec); + + (void) timezone_is_valid(str, LOG_DEBUG); + + return 0; +} diff --git a/src/fuzz/fuzz-udev-database.c b/src/fuzz/fuzz-udev-database.c new file mode 100644 index 000000000..9d7c0fa3e --- /dev/null +++ b/src/fuzz/fuzz-udev-database.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "device-internal.h" +#include "device-private.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_(sd_device_unrefp) sd_device *dev = NULL; + _cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-udev-database.XXXXXX"; + _cleanup_fclose_ FILE *f = NULL; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + assert_se(fmkostemp_safe(filename, "r+", &f) == 0); + if (size != 0) + assert_se(fwrite(data, size, 1, f) == 1); + + fflush(f); + assert_se(device_new_aux(&dev) >= 0); + (void) device_read_db_internal_filename(dev, filename); + return 0; +} diff --git a/src/fuzz/fuzz-unit-file.c b/src/fuzz/fuzz-unit-file.c index 93de50191..84b1ea66e 100644 --- a/src/fuzz/fuzz-unit-file.c +++ b/src/fuzz/fuzz-unit-file.c @@ -79,6 +79,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { assert_se(g); unit_dump(u, g, ""); + manager_dump(m, g, ">>>"); return 0; } diff --git a/src/fuzz/meson.build b/src/fuzz/meson.build index f628001a2..0d1ad2b1e 100644 --- a/src/fuzz/meson.build +++ b/src/fuzz/meson.build @@ -97,6 +97,10 @@ fuzzers += [ libshared], []], + [['src/fuzz/fuzz-udev-database.c'], + [libshared], + []], + [['src/fuzz/fuzz-udev-rules.c'], [libudev_core, libudev_static, @@ -108,4 +112,34 @@ fuzzers += [ [['src/fuzz/fuzz-compress.c'], [libshared], []], + + [['src/fuzz/fuzz-bus-label.c'], + [libshared], + []], + + [['src/fuzz/fuzz-env-file.c'], + [libshared], + []], + + [['src/fuzz/fuzz-hostname-util.c'], + [libshared], + []], + + [['src/fuzz/fuzz-nspawn-settings.c'], + [libshared, + libnspawn_core], + []], + + [['src/fuzz/fuzz-nspawn-oci.c'], + [libshared, + libnspawn_core], + []], + + [['src/fuzz/fuzz-calendarspec.c'], + [libshared], + []], + + [['src/fuzz/fuzz-time-util.c'], + [libshared], + []], ] diff --git a/src/gpt-auto-generator/gpt-auto-generator.c b/src/gpt-auto-generator/gpt-auto-generator.c index d9e29c47f..0f1e184ee 100644 --- a/src/gpt-auto-generator/gpt-auto-generator.c +++ b/src/gpt-auto-generator/gpt-auto-generator.c @@ -18,6 +18,7 @@ #include "efivars.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "fstab-util.h" #include "generator.h" #include "gpt.h" @@ -322,7 +323,6 @@ static int add_swap(const char *path) { return generator_add_symlink(arg_dest, SPECIAL_SWAP_TARGET, "wants", name); } -#if ENABLE_EFI static int add_automount( const char *id, const char *what, @@ -384,8 +384,43 @@ static int add_automount( return generator_add_symlink(arg_dest, SPECIAL_LOCAL_FS_TARGET, "wants", unit); } -static int add_esp(DissectedPartition *p) { - const char *esp; +static int add_xbootldr(DissectedPartition *p) { + int r; + + assert(p); + + if (in_initrd()) { + log_debug("In initrd, ignoring the XBOOTLDR partition."); + return 0; + } + + r = fstab_is_mount_point("/boot"); + if (r < 0) + return log_error_errno(r, "Failed to parse fstab: %m"); + if (r > 0) { + log_debug("/boot specified in fstab, ignoring XBOOTLDR partition."); + return 0; + } + + r = path_is_busy("/boot"); + if (r < 0) + return r; + if (r > 0) + return 0; + + return add_automount("boot", + p->node, + "/boot", + p->fstype, + true, + "umask=0077", + "Boot Loader Partition", + 120 * USEC_PER_SEC); +} + +#if ENABLE_EFI +static int add_esp(DissectedPartition *p, bool has_xbootldr) { + const char *esp_path = NULL, *id = NULL; int r; assert(p); @@ -395,21 +430,37 @@ static int add_esp(DissectedPartition *p) { return 0; } - /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice */ - esp = access("/efi/", F_OK) >= 0 ? "/efi" : "/boot"; + /* If /efi exists we'll use that. Otherwise we'll use /boot, as that's usually the better choice, but + * only if there's no explicit XBOOTLDR partition around. */ + if (access("/efi", F_OK) < 0) { + if (errno != ENOENT) + return log_error_errno(errno, "Failed to determine whether /efi exists: %m"); + + /* Use /boot as fallback, but only if there's no XBOOTLDR partition */ + if (!has_xbootldr) { + esp_path = "/boot"; + id = "boot"; + } + } + if (!esp_path) + esp_path = "/efi"; + if (!id) + id = "efi"; /* We create an .automount which is not overridden by the .mount from the fstab generator. */ - r = fstab_is_mount_point(esp); + r = fstab_is_mount_point(esp_path); if (r < 0) return log_error_errno(r, "Failed to parse fstab: %m"); if (r > 0) { - log_debug("%s specified in fstab, ignoring.", esp); + log_debug("%s specified in fstab, ignoring.", esp_path); return 0; } - r = path_is_busy(esp); - if (r != 0) - return r < 0 ? r : 0; + r = path_is_busy(esp_path); + if (r < 0) + return r; + if (r > 0) + return 0; if (is_efi_boot()) { sd_id128_t loader_uuid; @@ -425,15 +476,15 @@ static int add_esp(DissectedPartition *p) { return log_error_errno(r, "Failed to read ESP partition UUID: %m"); if (!sd_id128_equal(p->uuid, loader_uuid)) { - log_debug("Partition for %s does not appear to be the partition we are booted from.", esp); + log_debug("Partition for %s does not appear to be the partition we are booted from.", p->node); return 0; } } else log_debug("Not an EFI boot, skipping ESP check."); - return add_automount("boot", + return add_automount(id, p->node, - esp, + esp_path, p->fstype, true, "umask=0077", @@ -441,7 +492,7 @@ static int add_esp(DissectedPartition *p) { 120 * USEC_PER_SEC); } #else -static int add_esp(DissectedPartition *p) { +static int add_esp(DissectedPartition *p, bool has_xbootldr) { return 0; } #endif @@ -467,13 +518,13 @@ static int add_root_rw(DissectedPartition *p) { return 0; } + (void) generator_enable_remount_fs_service(arg_dest); + 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); @@ -483,7 +534,7 @@ static int add_root_rw(DissectedPartition *p) { return 0; } -static int open_parent(dev_t devnum, int *ret) { +static int open_parent_devno(dev_t devnum, int *ret) { _cleanup_(sd_device_unrefp) sd_device *d = NULL; const char *name, *devtype, *node; sd_device *parent; @@ -551,7 +602,7 @@ static int enumerate_partitions(dev_t devnum) { _cleanup_(dissected_image_unrefp) DissectedImage *m = NULL; int r, k; - r = open_parent(devnum, &fd); + r = open_parent_devno(devnum, &fd); if (r <= 0) return r; @@ -569,8 +620,14 @@ static int enumerate_partitions(dev_t devnum) { r = k; } + if (m->partitions[PARTITION_XBOOTLDR].found) { + k = add_xbootldr(m->partitions + PARTITION_XBOOTLDR); + if (k < 0) + r = k; + } + if (m->partitions[PARTITION_ESP].found) { - k = add_esp(m->partitions + PARTITION_ESP); + k = add_esp(m->partitions + PARTITION_ESP, m->partitions[PARTITION_XBOOTLDR].found); if (k < 0) r = k; } @@ -678,6 +735,9 @@ static int add_root_mount(void) { return r; } + /* Note that we do not need to enable systemd-remount-fs.service here. If + * /etc/fstab exists, systemd-fstab-generator will pull it in for us. */ + return add_mount( "root", "/dev/gpt-auto-root", @@ -704,8 +764,25 @@ static int add_mounts(void) { if (r < 0) return log_error_errno(r, "Failed to determine block device of /usr file system: %m"); if (r == 0) { - log_debug("Neither root nor /usr file system are on a (single) block device."); - return 0; + _cleanup_free_ char *p = NULL; + mode_t m; + + /* If the root mount has been replaced by some form of volatile file system (overlayfs), the + * original root block device node is symlinked in /run/systemd/volatile-root. Let's read that + * here. */ + r = readlink_malloc("/run/systemd/volatile-root", &p); + if (r == -ENOENT) { + log_debug("Neither root nor /usr file system are on a (single) block device."); + return 0; + } + if (r < 0) + return log_error_errno(r, "Failed to read symlink /run/systemd/volatile-root: %m"); + + r = device_path_parse_major_minor(p, &m, &devno); + if (r < 0) + return log_error_errno(r, "Failed to parse major/minor device node: %m"); + if (!S_ISBLK(m)) + return log_error_errno(SYNTHETIC_ERRNO(ENOTBLK), "Volatile root device is of wrong type."); } } diff --git a/src/hibernate-resume/hibernate-resume-generator.c b/src/hibernate-resume/hibernate-resume-generator.c index 8b127ca85..02eb0c9a5 100644 --- a/src/hibernate-resume/hibernate-resume-generator.c +++ b/src/hibernate-resume/hibernate-resume-generator.c @@ -2,6 +2,7 @@ #include #include +#include #include "alloc-util.h" #include "fstab-util.h" @@ -13,7 +14,6 @@ #include "special.h" #include "string-util.h" #include "unit-name.h" -#include "util.h" static const char *arg_dest = "/tmp"; static char *arg_resume_device = NULL; diff --git a/src/hostname/hostnamed.c b/src/hostname/hostnamed.c index 7777450c3..c35ef5566 100644 --- a/src/hostname/hostnamed.c +++ b/src/hostname/hostnamed.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "alloc-util.h" @@ -19,6 +21,7 @@ #include "main-func.h" #include "missing_capability.h" #include "nscd-flush.h" +#include "nulstr-util.h" #include "os-util.h" #include "parse-util.h" #include "path-util.h" diff --git a/src/id128/id128.c b/src/id128/id128.c index 06049e7ba..60551ec22 100644 --- a/src/id128/id128.c +++ b/src/id128/id128.c @@ -70,7 +70,7 @@ static int help(void) { if (r < 0) return log_oom(); - printf("%s [OPTIONS...] {COMMAND} ...\n\n" + 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" diff --git a/src/import/curl-util.c b/src/import/curl-util.c index 7db03b291..cd6822f8d 100644 --- a/src/import/curl-util.c +++ b/src/import/curl-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "build.h" #include "curl-util.h" diff --git a/src/import/curl-util.h b/src/import/curl-util.h index a03e84403..6a7be6421 100644 --- a/src/import/curl-util.h +++ b/src/import/curl-util.h @@ -7,6 +7,7 @@ #include "sd-event.h" #include "hashmap.h" +#include "time-util.h" typedef struct CurlGlue CurlGlue; diff --git a/src/import/export-raw.c b/src/import/export-raw.c index 6a02b47a1..c1c946cd2 100644 --- a/src/import/export-raw.c +++ b/src/import/export-raw.c @@ -223,7 +223,7 @@ static int raw_export_process(RawExport *e) { finish: if (r >= 0) { - (void) copy_times(e->input_fd, e->output_fd); + (void) copy_times(e->input_fd, e->output_fd, COPY_CRTIME); (void) copy_xattr(e->input_fd, e->output_fd); } diff --git a/src/import/export.c b/src/import/export.c index 490710678..77d24b863 100644 --- a/src/import/export.c +++ b/src/import/export.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "sd-event.h" #include "sd-id128.h" @@ -77,8 +78,7 @@ static int export_tar(int argc, char *argv[], void *userdata) { if (argc >= 3) path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); determine_compression_from_filename(path); @@ -154,8 +154,7 @@ static int export_raw(int argc, char *argv[], void *userdata) { if (argc >= 3) path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); determine_compression_from_filename(path); diff --git a/src/import/import-common.c b/src/import/import-common.c index 89f03010d..1f63ebb76 100644 --- a/src/import/import-common.c +++ b/src/import/import-common.c @@ -15,6 +15,7 @@ #include "import-common.h" #include "os-util.h" #include "process-util.h" +#include "selinux-util.h" #include "signal-util.h" #include "tmpfile-util.h" #include "util.h" @@ -62,6 +63,7 @@ int import_make_read_only(const char *path) { int import_fork_tar_x(const char *path, pid_t *ret) { _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; + bool use_selinux; pid_t pid; int r; @@ -71,6 +73,8 @@ int import_fork_tar_x(const char *path, pid_t *ret) { if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); + use_selinux = mac_selinux_use(); + r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) return r; @@ -100,7 +104,8 @@ int import_fork_tar_x(const char *path, pid_t *ret) { if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", NULL); + execlp("tar", "tar", "--numeric-owner", "-C", path, "-px", "--xattrs", "--xattrs-include=*", + use_selinux ? "--selinux" : "--no-selinux", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } @@ -112,6 +117,7 @@ int import_fork_tar_x(const char *path, pid_t *ret) { int import_fork_tar_c(const char *path, pid_t *ret) { _cleanup_close_pair_ int pipefd[2] = { -1, -1 }; + bool use_selinux; pid_t pid; int r; @@ -121,6 +127,8 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (pipe2(pipefd, O_CLOEXEC) < 0) return log_error_errno(errno, "Failed to create pipe for tar: %m"); + use_selinux = mac_selinux_use(); + r = safe_fork("(tar)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid); if (r < 0) return r; @@ -144,7 +152,8 @@ int import_fork_tar_c(const char *path, pid_t *ret) { if (r < 0) log_error_errno(r, "Failed to drop capabilities, ignoring: %m"); - execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", ".", NULL); + execlp("tar", "tar", "-C", path, "-c", "--xattrs", "--xattrs-include=*", + use_selinux ? "--selinux" : "--no-selinux", ".", NULL); log_error_errno(errno, "Failed to execute tar: %m"); _exit(EXIT_FAILURE); } diff --git a/src/import/import-fs.c b/src/import/import-fs.c index 35ba6ba38..04344492c 100644 --- a/src/import/import-fs.c +++ b/src/import/import-fs.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "btrfs-util.h" @@ -116,15 +117,13 @@ static int import_fs(int argc, char *argv[], void *userdata) { if (argc >= 2) path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); if (argc >= 3) local = argv[2]; else if (path) local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { if (!machine_name_is_valid(local)) { diff --git a/src/import/import-raw.c b/src/import/import-raw.c index 4b1161557..b90583260 100644 --- a/src/import/import-raw.c +++ b/src/import/import-raw.c @@ -183,7 +183,7 @@ static int raw_import_maybe_convert_qcow2(RawImport *i) { r = qcow2_convert(i->output_fd, converted_fd); if (r < 0) { - unlink(t); + (void) unlink(t); return log_error_errno(r, "Failed to convert qcow2 image: %m"); } @@ -215,7 +215,7 @@ static int raw_import_finish(RawImport *i) { return r; if (S_ISREG(i->st.st_mode)) { - (void) copy_times(i->input_fd, i->output_fd); + (void) copy_times(i->input_fd, i->output_fd, COPY_CRTIME); (void) copy_xattr(i->input_fd, i->output_fd); } diff --git a/src/import/import.c b/src/import/import.c index f34244acf..cc2855745 100644 --- a/src/import/import.c +++ b/src/import/import.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "sd-event.h" #include "sd-id128.h" @@ -48,15 +49,13 @@ static int import_tar(int argc, char *argv[], void *userdata) { if (argc >= 2) path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); if (argc >= 3) local = argv[2]; else if (path) local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { r = tar_strip_suffixes(local, &ll); @@ -144,15 +143,13 @@ static int import_raw(int argc, char *argv[], void *userdata) { if (argc >= 2) path = argv[1]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); if (argc >= 3) local = argv[2]; else if (path) local = basename(path); - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { r = raw_strip_suffixes(local, &ll); diff --git a/src/import/pull-job.c b/src/import/pull-job.c index 6881bd627..9f759a77e 100644 --- a/src/import/pull-job.c +++ b/src/import/pull-job.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include #include #include "alloc-util.h" diff --git a/src/import/pull-raw.c b/src/import/pull-raw.c index 3a3e015df..4f76421bc 100644 --- a/src/import/pull-raw.c +++ b/src/import/pull-raw.c @@ -250,7 +250,7 @@ static int raw_pull_maybe_convert_qcow2(RawPull *i) { r = qcow2_convert(i->raw_job->disk_fd, converted_fd); if (r < 0) { - unlink(t); + (void) unlink(t); return log_error_errno(r, "Failed to convert qcow2 image: %m"); } @@ -299,7 +299,7 @@ static int raw_pull_copy_auxiliary_file( local = strjoina(i->image_root, "/", i->local, suffix); - r = copy_file_atomic(*path, local, 0644, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); + r = copy_file_atomic(*path, local, 0644, 0, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); if (r == -EEXIST) log_warning_errno(r, "File %s already exists, not replacing.", local); else if (r == -ENOENT) @@ -364,11 +364,11 @@ static int raw_pull_make_local_copy(RawPull *i) { r = copy_bytes(i->raw_job->disk_fd, dfd, (uint64_t) -1, COPY_REFLINK); if (r < 0) { - unlink(tp); + (void) unlink(tp); return log_error_errno(r, "Failed to make writable copy of image: %m"); } - (void) copy_times(i->raw_job->disk_fd, dfd); + (void) copy_times(i->raw_job->disk_fd, dfd, COPY_CRTIME); (void) copy_xattr(i->raw_job->disk_fd, dfd); dfd = safe_close(dfd); @@ -376,7 +376,7 @@ static int raw_pull_make_local_copy(RawPull *i) { r = rename(tp, p); if (r < 0) { r = log_error_errno(errno, "Failed to move writable image into place: %m"); - unlink(tp); + (void) unlink(tp); return r; } diff --git a/src/import/pull-tar.c b/src/import/pull-tar.c index e7a208e90..3930578a8 100644 --- a/src/import/pull-tar.c +++ b/src/import/pull-tar.c @@ -244,7 +244,7 @@ static int tar_pull_make_local_copy(TarPull *i) { local_settings = strjoina(i->image_root, "/", i->local, ".nspawn"); - r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); + r = copy_file_atomic(i->settings_path, local_settings, 0664, 0, 0, COPY_REFLINK | (i->force_local ? COPY_REPLACE : 0)); if (r == -EEXIST) log_warning_errno(r, "Settings file %s already exists, not replacing.", local_settings); else if (r == -ENOENT) diff --git a/src/import/pull.c b/src/import/pull.c index 337699258..7e8712493 100644 --- a/src/import/pull.c +++ b/src/import/pull.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "sd-event.h" #include "sd-id128.h" @@ -63,8 +64,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { local = l; } - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { r = tar_strip_suffixes(local, &ll); @@ -150,8 +150,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { local = l; } - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { r = raw_strip_suffixes(local, &ll); diff --git a/src/import/test-qcow2.c b/src/import/test-qcow2.c index bd2b45821..1703cfd16 100644 --- a/src/import/test-qcow2.c +++ b/src/import/test-qcow2.c @@ -1,9 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "fd-util.h" #include "log.h" #include "qcow2-util.h" -#include "util.h" int main(int argc, char *argv[]) { _cleanup_close_ int sfd = -1, dfd = -1; diff --git a/src/initctl/initctl.c b/src/initctl/initctl.c index 260dc2eb6..96ea942b5 100644 --- a/src/initctl/initctl.c +++ b/src/initctl/initctl.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include "sd-bus.h" @@ -18,9 +20,9 @@ #include "initreq.h" #include "list.h" #include "log.h" -#include "special.h" -#include "util.h" +#include "memory-util.h" #include "process-util.h" +#include "special.h" #define SERVER_FD_MAX 16 #define TIMEOUT_MSEC ((int) (DEFAULT_EXIT_USEC/USEC_PER_MSEC)) diff --git a/src/journal-remote/journal-gatewayd.c b/src/journal-remote/journal-gatewayd.c index af45fa549..734e8c7f3 100644 --- a/src/journal-remote/journal-gatewayd.c +++ b/src/journal-remote/journal-gatewayd.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include "sd-bus.h" diff --git a/src/journal-remote/journal-remote-main.c b/src/journal-remote/journal-remote-main.c index 802c3ea60..2321a91e7 100644 --- a/src/journal-remote/journal-remote-main.c +++ b/src/journal-remote/journal-remote-main.c @@ -265,6 +265,7 @@ static int request_handler( const char *header; int r, code, fd; _cleanup_free_ char *hostname = NULL; + bool chunked = false; size_t len; assert(connection); @@ -290,21 +291,33 @@ static int request_handler( return mhd_respond(connection, MHD_HTTP_UNSUPPORTED_MEDIA_TYPE, "Content-Type: application/vnd.fdo.journal is required."); - header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length"); - if (!header) - return mhd_respond(connection, MHD_HTTP_LENGTH_REQUIRED, - "Content-Length header is required."); - r = safe_atozu(header, &len); - if (r < 0) - return mhd_respondf(connection, r, MHD_HTTP_LENGTH_REQUIRED, - "Content-Length: %s cannot be parsed: %m", header); + header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Transfer-Encoding"); + if (header) { + if (!strcaseeq(header, "chunked")) + return mhd_respondf(connection, 0, MHD_HTTP_BAD_REQUEST, + "Unsupported Transfer-Encoding type: %s", header); - if (len > ENTRY_SIZE_MAX) - /* When serialized, an entry of maximum size might be slightly larger, - * so this does not correspond exactly to the limit in journald. Oh well. - */ - return mhd_respondf(connection, 0, MHD_HTTP_PAYLOAD_TOO_LARGE, - "Payload larger than maximum size of %u bytes", ENTRY_SIZE_MAX); + chunked = true; + } + + header = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, "Content-Length"); + if (header) { + if (chunked) + return mhd_respond(connection, MHD_HTTP_BAD_REQUEST, + "Content-Length must not specified when Transfer-Encoding type is 'chuncked'"); + + r = safe_atozu(header, &len); + if (r < 0) + return mhd_respondf(connection, r, MHD_HTTP_LENGTH_REQUIRED, + "Content-Length: %s cannot be parsed: %m", header); + + if (len > ENTRY_SIZE_MAX) + /* When serialized, an entry of maximum size might be slightly larger, + * so this does not correspond exactly to the limit in journald. Oh well. + */ + return mhd_respondf(connection, 0, MHD_HTTP_PAYLOAD_TOO_LARGE, + "Payload larger than maximum size of %u bytes", ENTRY_SIZE_MAX); + } { const union MHD_ConnectionInfo *ci; diff --git a/src/journal-remote/journal-remote-parse.c b/src/journal-remote/journal-remote-parse.c index 535d06ac7..ebcae2fca 100644 --- a/src/journal-remote/journal-remote-parse.c +++ b/src/journal-remote/journal-remote-parse.c @@ -68,7 +68,11 @@ int process_source(RemoteSource *source, bool compress, bool seal) { assert(source->importer.iovw.iovec); - r = writer_write(source->writer, &source->importer.iovw, &source->importer.ts, compress, seal); + r = writer_write(source->writer, + &source->importer.iovw, + &source->importer.ts, + &source->importer.boot_id, + compress, seal); if (r == -EBADMSG) { log_error_errno(r, "Entry is invalid, ignoring."); r = 0; diff --git a/src/journal-remote/journal-remote-write.c b/src/journal-remote/journal-remote-write.c index 188ff3582..ab5e03ab5 100644 --- a/src/journal-remote/journal-remote-write.c +++ b/src/journal-remote/journal-remote-write.c @@ -59,6 +59,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(Writer, writer, writer_free); int writer_write(Writer *w, struct iovec_wrapper *iovw, dual_timestamp *ts, + sd_id128_t *boot_id, bool compress, bool seal) { int r; @@ -75,7 +76,7 @@ int writer_write(Writer *w, return r; } - r = journal_file_append_entry(w->journal, ts, NULL, + r = journal_file_append_entry(w->journal, ts, boot_id, iovw->iovec, iovw->count, &w->seqnum, NULL, NULL); if (r >= 0) { @@ -93,7 +94,7 @@ int writer_write(Writer *w, log_debug("%s: Successfully rotated journal", w->journal->path); log_debug("Retrying write."); - r = journal_file_append_entry(w->journal, ts, NULL, + r = journal_file_append_entry(w->journal, ts, boot_id, iovw->iovec, iovw->count, &w->seqnum, NULL, NULL); if (r < 0) diff --git a/src/journal-remote/journal-remote-write.h b/src/journal-remote/journal-remote-write.h index e445859ec..d42256e67 100644 --- a/src/journal-remote/journal-remote-write.h +++ b/src/journal-remote/journal-remote-write.h @@ -28,6 +28,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Writer*, writer_unref); int writer_write(Writer *s, struct iovec_wrapper *iovw, dual_timestamp *ts, + sd_id128_t *boot_id, bool compress, bool seal); diff --git a/src/journal-remote/journal-remote.c b/src/journal-remote/journal-remote.c index 1da32c5f8..04c66a29c 100644 --- a/src/journal-remote/journal-remote.c +++ b/src/journal-remote/journal-remote.c @@ -13,6 +13,7 @@ #include "alloc-util.h" #include "def.h" +#include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "journal-file.h" @@ -466,14 +467,23 @@ static int dispatch_blocking_source_event(sd_event_source *event, return journal_remote_handle_raw_source(event, source->importer.fd, EPOLLIN, journal_remote_server_global); } -static int accept_connection(const char* type, int fd, - SocketAddress *addr, char **hostname) { - int fd2, r; +static int accept_connection( + const char* type, + int fd, + SocketAddress *addr, + char **hostname) { + + _cleanup_close_ int fd2 = -1; + int r; log_debug("Accepting new %s connection on fd:%d", type, fd); fd2 = accept4(fd, &addr->sockaddr.sa, &addr->size, SOCK_NONBLOCK|SOCK_CLOEXEC); - if (fd2 < 0) + if (fd2 < 0) { + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + return -EAGAIN; + return log_error_errno(errno, "accept() on fd:%d failed: %m", fd); + } switch(socket_address_family(addr)) { case AF_INET: @@ -482,18 +492,12 @@ static int accept_connection(const char* type, int fd, char *b; r = socket_address_print(addr, &a); - if (r < 0) { - log_error_errno(r, "socket_address_print(): %m"); - close(fd2); - return r; - } + if (r < 0) + return log_error_errno(r, "socket_address_print(): %m"); r = socknameinfo_pretty(&addr->sockaddr, addr->size, &b); - if (r < 0) { - log_error_errno(r, "Resolving hostname failed: %m"); - close(fd2); - return r; - } + if (r < 0) + return log_error_errno(r, "Resolving hostname failed: %m"); log_debug("Accepted %s %s connection from %s", type, @@ -501,22 +505,22 @@ static int accept_connection(const char* type, int fd, a); *hostname = b; + return TAKE_FD(fd2); + } - return fd2; - }; default: - log_error("Rejected %s connection with unsupported family %d", - type, socket_address_family(addr)); - close(fd2); - - return -EINVAL; + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Rejected %s connection with unsupported family %d", + type, socket_address_family(addr)); } } -static int dispatch_raw_connection_event(sd_event_source *event, - int fd, - uint32_t revents, - void *userdata) { +static int dispatch_raw_connection_event( + sd_event_source *event, + int fd, + uint32_t revents, + void *userdata) { + RemoteServer *s = userdata; int fd2; SocketAddress addr = { @@ -526,6 +530,8 @@ static int dispatch_raw_connection_event(sd_event_source *event, char *hostname = NULL; fd2 = accept_connection("raw", fd, &addr, &hostname); + if (fd2 == -EAGAIN) + return 0; if (fd2 < 0) return fd2; diff --git a/src/journal-remote/journal-upload.c b/src/journal-remote/journal-upload.c index ef3556f82..cc9682528 100644 --- a/src/journal-remote/journal-upload.c +++ b/src/journal-remote/journal-upload.c @@ -106,7 +106,7 @@ static int check_cursor_updating(Uploader *u) { if (r < 0) return log_error_errno(r, "Cannot save state to %s: %m", u->state_file); - unlink(temp_path); + (void) unlink(temp_path); return 0; } diff --git a/src/journal/catalog.c b/src/journal/catalog.c index 4062f12c2..7beffc1e1 100644 --- a/src/journal/catalog.c +++ b/src/journal/catalog.c @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include "sd-id128.h" @@ -17,15 +19,16 @@ #include "fileio.h" #include "hashmap.h" #include "log.h" +#include "memory-util.h" #include "mkdir.h" #include "path-util.h" #include "siphash24.h" +#include "sort-util.h" #include "sparse-endian.h" #include "strbuf.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" -#include "util.h" const char * const catalog_file_dirs[] = { "/usr/local/lib/systemd/catalog/", @@ -33,7 +36,7 @@ const char * const catalog_file_dirs[] = { NULL }; -#define CATALOG_SIGNATURE (uint8_t[]) { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' } +#define CATALOG_SIGNATURE { 'R', 'H', 'H', 'H', 'K', 'S', 'L', 'P' } typedef struct CatalogHeader { uint8_t signature[8]; /* "RHHHKSLP" */ @@ -209,34 +212,38 @@ int catalog_file_lang(const char* filename, char **lang) { return 1; } -static int catalog_entry_lang(const char* filename, int line, - const char* t, const char* deflang, char **lang) { +static int catalog_entry_lang( + const char* filename, + unsigned line, + const char* t, + const char* deflang, + char **ret) { + size_t c; + char *z; c = strlen(t); if (c < 2) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "[%s:%u] Language too short.", - filename, line); + "[%s:%u] Language too short.", filename, line); if (c > 31) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "[%s:%u] language too long.", filename, - line); + "[%s:%u] language too long.", filename, line); if (deflang) { if (streq(t, deflang)) { - log_warning("[%s:%u] language specified unnecessarily", - filename, line); + log_warning("[%s:%u] language specified unnecessarily", filename, line); return 0; - } else - log_warning("[%s:%u] language differs from default for file", - filename, line); + } + + log_warning("[%s:%u] language differs from default for file", filename, line); } - *lang = strdup(t); - if (!*lang) - return -ENOMEM; + z = strdup(t); + if (!z) + return -ENOMEM; + *ret = z; return 0; } @@ -367,32 +374,33 @@ int catalog_import_file(Hashmap *h, const char *path) { return 0; } -static int64_t write_catalog(const char *database, struct strbuf *sb, - CatalogItem *items, size_t n) { - CatalogHeader header; +static int64_t write_catalog( + const char *database, + struct strbuf *sb, + CatalogItem *items, + size_t n) { + _cleanup_fclose_ FILE *w = NULL; - int r; - _cleanup_free_ char *d, *p = NULL; + _cleanup_free_ char *p = NULL; + CatalogHeader header; size_t k; + int r; - d = dirname_malloc(database); - if (!d) - return log_oom(); - - r = mkdir_p(d, 0775); + r = mkdir_parents(database, 0755); if (r < 0) - return log_error_errno(r, "Recursive mkdir %s: %m", d); + return log_error_errno(r, "Failed to create parent directories of %s: %m", database); r = fopen_temporary(database, &w, &p); if (r < 0) return log_error_errno(r, "Failed to open database for writing: %s: %m", database); - zero(header); - memcpy(header.signature, CATALOG_SIGNATURE, sizeof(header.signature)); - header.header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)); - header.catalog_item_size = htole64(sizeof(CatalogItem)); - header.n_items = htole64(n); + header = (CatalogHeader) { + .signature = CATALOG_SIGNATURE, + .header_size = htole64(ALIGN_TO(sizeof(CatalogHeader), 8)), + .catalog_item_size = htole64(sizeof(CatalogItem)), + .n_items = htole64(n), + }; r = -EIO; @@ -420,7 +428,7 @@ static int64_t write_catalog(const char *database, struct strbuf *sb, goto error; } - fchmod(fileno(w), 0644); + (void) fchmod(fileno(w), 0644); if (rename(p, database) < 0) { r = log_error_errno(errno, "rename (%s -> %s) failed: %m", p, database); @@ -503,10 +511,10 @@ int catalog_update(const char* database, const char* root, const char* const* di } static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p) { + _cleanup_close_ int fd = -1; const CatalogHeader *h; - int fd; - void *p; struct stat st; + void *p; assert(_fd); assert(_st); @@ -516,35 +524,28 @@ static int open_mmap(const char *database, int *_fd, struct stat *_st, void **_p if (fd < 0) return -errno; - if (fstat(fd, &st) < 0) { - safe_close(fd); + if (fstat(fd, &st) < 0) return -errno; - } - if (st.st_size < (off_t) sizeof(CatalogHeader)) { - safe_close(fd); + if (st.st_size < (off_t) sizeof(CatalogHeader)) return -EINVAL; - } p = mmap(NULL, PAGE_ALIGN(st.st_size), PROT_READ, MAP_SHARED, fd, 0); - if (p == MAP_FAILED) { - safe_close(fd); + if (p == MAP_FAILED) return -errno; - } h = p; - if (memcmp(h->signature, CATALOG_SIGNATURE, sizeof(h->signature)) != 0 || + if (memcmp(h->signature, (const uint8_t[]) CATALOG_SIGNATURE, sizeof(h->signature)) != 0 || le64toh(h->header_size) < sizeof(CatalogHeader) || le64toh(h->catalog_item_size) < sizeof(CatalogItem) || h->incompatible_flags != 0 || le64toh(h->n_items) <= 0 || st.st_size < (off_t) (le64toh(h->header_size) + le64toh(h->catalog_item_size) * le64toh(h->n_items))) { - safe_close(fd); munmap(p, st.st_size); return -EBADMSG; } - *_fd = fd; + *_fd = TAKE_FD(fd); *_st = st; *_p = p; diff --git a/src/journal/catalog.h b/src/journal/catalog.h index 4e6f161af..20497d118 100644 --- a/src/journal/catalog.h +++ b/src/journal/catalog.h @@ -2,6 +2,7 @@ #pragma once #include +#include #include "sd-id128.h" diff --git a/src/journal/compress.c b/src/journal/compress.c index e95ce2bca..7a79e566b 100644 --- a/src/journal/compress.c +++ b/src/journal/compress.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #if HAVE_XZ diff --git a/src/journal/fsprg.c b/src/journal/fsprg.c index 6d062de5f..d60fc5006 100644 --- a/src/journal/fsprg.c +++ b/src/journal/fsprg.c @@ -32,6 +32,7 @@ #include "fsprg.h" #include "gcrypt-util.h" +#include "memory-util.h" #define ISVALID_SECPAR(secpar) (((secpar) % 16 == 0) && ((secpar) >= 16) && ((secpar) <= 16384)) #define VALIDATE_SECPAR(secpar) assert(ISVALID_SECPAR(secpar)); diff --git a/src/journal/journal-authenticate.c b/src/journal/journal-authenticate.c index 8a9ce8a0d..da275c251 100644 --- a/src/journal/journal-authenticate.c +++ b/src/journal/journal-authenticate.c @@ -10,6 +10,8 @@ #include "journal-authenticate.h" #include "journal-def.h" #include "journal-file.h" +#include "memory-util.h" +#include "time-util.h" static uint64_t journal_file_tag_seqnum(JournalFile *f) { uint64_t r; diff --git a/src/journal/journal-file.c b/src/journal/journal-file.c index 56827f9f3..91d1c2921 100644 --- a/src/journal/journal-file.c +++ b/src/journal/journal-file.c @@ -22,10 +22,12 @@ #include "journal-def.h" #include "journal-file.h" #include "lookup3.h" +#include "memory-util.h" #include "parse-util.h" #include "path-util.h" #include "random-util.h" #include "set.h" +#include "sort-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" @@ -237,6 +239,9 @@ int journal_file_set_offline(JournalFile *f, bool wait) { int k; assert_se(sigfillset(&ss) >= 0); + /* Don't block SIGBUS since the offlining thread accesses a memory mapped file. + * Asynchronous SIGBUS signals can safely be handled by either thread. */ + assert_se(sigdelset(&ss, SIGBUS) >= 0); r = pthread_sigmask(SIG_BLOCK, &ss, &saved_ss); if (r > 0) @@ -1791,7 +1796,9 @@ static int journal_file_append_entry_internal( o->entry.realtime = htole64(ts->realtime); o->entry.monotonic = htole64(ts->monotonic); o->entry.xor_hash = htole64(xor_hash); - o->entry.boot_id = boot_id ? *boot_id : f->header->boot_id; + if (boot_id) + f->header->boot_id = *boot_id; + o->entry.boot_id = f->header->boot_id; #if HAVE_GCRYPT r = journal_file_hmac_put_object(f, OBJECT_ENTRY, o, np); diff --git a/src/journal/journal-file.h b/src/journal/journal-file.h index 29e324d8c..e1ea36899 100644 --- a/src/journal/journal-file.h +++ b/src/journal/journal-file.h @@ -2,6 +2,7 @@ #pragma once #include +#include #if HAVE_GCRYPT # include @@ -12,9 +13,9 @@ #include "hashmap.h" #include "journal-def.h" -#include "macro.h" #include "mmap-cache.h" #include "sparse-endian.h" +#include "time-util.h" typedef struct JournalMetrics { /* For all these: -1 means "pick automatically", and 0 means "no limit enforced" */ diff --git a/src/journal/journal-send.c b/src/journal/journal-send.c index 861845413..5ef11fa1a 100644 --- a/src/journal/journal-send.c +++ b/src/journal/journal-send.c @@ -13,6 +13,7 @@ #include "sd-journal.h" #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "io-util.h" #include "memfd-util.h" @@ -20,7 +21,6 @@ #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 2778ce40c..a932314e1 100644 --- a/src/journal/journal-vacuum.c +++ b/src/journal/journal-vacuum.c @@ -14,9 +14,9 @@ #include "journal-file.h" #include "journal-vacuum.h" #include "parse-util.h" +#include "sort-util.h" #include "string-util.h" #include "time-util.h" -#include "util.h" #include "xattr-util.h" struct vacuum_info { diff --git a/src/journal/journal-verify.c b/src/journal/journal-verify.c index 5eff80a99..0a06ce7b8 100644 --- a/src/journal/journal-verify.c +++ b/src/journal/journal-verify.c @@ -65,7 +65,7 @@ static uint64_t scale_progress(uint64_t scale, uint64_t p, uint64_t m) { * Currently all callers use m >= 1, but we keep the check to be defensive. */ - if (p >= m || m == 0) /* lgtm [cpp/constant-comparison] */ + 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 14a02eda7..00489098e 100644 --- a/src/journal/journalctl.c +++ b/src/journal/journalctl.c @@ -50,7 +50,9 @@ #include "locale-util.h" #include "log.h" #include "logs-show.h" +#include "memory-util.h" #include "mkdir.h" +#include "nulstr-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -119,6 +121,7 @@ static int arg_boot_offset = 0; static bool arg_dmesg = false; static bool arg_no_hostname = false; static const char *arg_cursor = NULL; +static const char *arg_cursor_file = NULL; static const char *arg_after_cursor = NULL; static bool arg_show_cursor = false; static const char *arg_directory = NULL; @@ -263,7 +266,11 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset sd_id128_t id = SD_ID128_NULL; int off = 0, r; - if (strlen(x) >= 32) { + if (streq(x, "all")) { + *boot_id = SD_ID128_NULL; + *offset = 0; + return 0; + } else if (strlen(x) >= 32) { char *t; t = strndupa(x, 32); @@ -291,7 +298,7 @@ static int parse_boot_descriptor(const char *x, sd_id128_t *boot_id, int *offset if (offset) *offset = off; - return 0; + return 1; } static int help(void) { @@ -315,6 +322,7 @@ static int help(void) { " -c --cursor=CURSOR Show entries starting at the specified cursor\n" " --after-cursor=CURSOR Show entries after the specified cursor\n" " --show-cursor Print the cursor after all the entries\n" + " --cursor-file=FILE Show entries after cursor in FILE and update FILE\n" " -b --boot[=ID] Show current boot or the specified boot\n" " --list-boots Show terse information about recorded boots\n" " -k --dmesg Show kernel message log from the current boot\n" @@ -396,6 +404,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_VERIFY_KEY, ARG_DISK_USAGE, ARG_AFTER_CURSOR, + ARG_CURSOR_FILE, ARG_SHOW_CURSOR, ARG_USER_UNIT, ARG_LIST_CATALOG, @@ -450,6 +459,7 @@ static int parse_argv(int argc, char *argv[]) { { "verify-key", required_argument, NULL, ARG_VERIFY_KEY }, { "disk-usage", no_argument, NULL, ARG_DISK_USAGE }, { "cursor", required_argument, NULL, 'c' }, + { "cursor-file", required_argument, NULL, ARG_CURSOR_FILE }, { "after-cursor", required_argument, NULL, ARG_AFTER_CURSOR }, { "show-cursor", no_argument, NULL, ARG_SHOW_CURSOR }, { "since", required_argument, NULL, 'S' }, @@ -587,30 +597,34 @@ static int parse_argv(int argc, char *argv[]) { case ARG_THIS_BOOT: arg_boot = true; + arg_boot_id = SD_ID128_NULL; + arg_boot_offset = 0; break; case 'b': arg_boot = true; + arg_boot_id = SD_ID128_NULL; + arg_boot_offset = 0; if (optarg) { r = parse_boot_descriptor(optarg, &arg_boot_id, &arg_boot_offset); - if (r < 0) { - log_error("Failed to parse boot descriptor '%s'", optarg); - return -EINVAL; - } - } else { + if (r < 0) + return log_error_errno(r, "Failed to parse boot descriptor '%s'", optarg); - /* Hmm, no argument? Maybe the next - * word on the command line is - * supposed to be the argument? Let's - * see if there is one and is parsable - * as a boot descriptor... */ + arg_boot = r; - if (optind < argc && - parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset) >= 0) + /* Hmm, no argument? Maybe the next + * word on the command line is + * supposed to be the argument? Let's + * see if there is one and is parsable + * as a boot descriptor... */ + } else if (optind < argc) { + r = parse_boot_descriptor(argv[optind], &arg_boot_id, &arg_boot_offset); + if (r >= 0) { + arg_boot = r; optind++; + } } - break; case ARG_LIST_BOOTS: @@ -661,6 +675,10 @@ static int parse_argv(int argc, char *argv[]) { arg_cursor = optarg; break; + case ARG_CURSOR_FILE: + arg_cursor_file = optarg; + break; + case ARG_AFTER_CURSOR: arg_after_cursor = optarg; break; @@ -1823,7 +1841,7 @@ finish: safe_close(fd); if (k) { - unlink(k); + (void) unlink(k); free(k); } @@ -1883,6 +1901,21 @@ static int verify(sd_journal *j) { return r; } +static int watch_run_systemd_journal(uint32_t mask) { + _cleanup_close_ int watch_fd = -1; + + (void) mkdir_p("/run/systemd/journal", 0755); + + watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (watch_fd < 0) + return log_error_errno(errno, "Failed to create inotify object: %m"); + + if (inotify_add_watch(watch_fd, "/run/systemd/journal", mask) < 0) + return log_error_errno(errno, "Failed to watch \"/run/systemd/journal\": %m"); + + return TAKE_FD(watch_fd); +} + static int flush_to_var(void) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; @@ -1916,19 +1949,13 @@ static int flush_to_var(void) { if (r < 0) return log_error_errno(r, "Failed to kill journal service: %s", bus_error_message(&error, r)); - mkdir_p("/run/systemd/journal", 0755); - - watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + watch_fd = watch_run_systemd_journal(IN_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR); if (watch_fd < 0) - return log_error_errno(errno, "Failed to create inotify watch: %m"); - - r = inotify_add_watch(watch_fd, "/run/systemd/journal", IN_CREATE|IN_DONT_FOLLOW|IN_ONLYDIR); - if (r < 0) - return log_error_errno(errno, "Failed to watch journal directory: %m"); + return watch_fd; for (;;) { if (access("/run/systemd/journal/flushed", F_OK) >= 0) - break; + return 0; if (errno != ENOENT) return log_error_errno(errno, "Failed to check for existence of /run/systemd/journal/flushed: %m"); @@ -1941,8 +1968,6 @@ static int flush_to_var(void) { if (r < 0) return log_error_errno(r, "Failed to flush inotify events: %m"); } - - return 0; } static int send_signal_and_wait(int sig, const char *watch_path) { @@ -1998,23 +2023,15 @@ static int send_signal_and_wait(int sig, const char *watch_path) { /* Let's install the inotify watch, if we didn't do that yet. */ if (watch_fd < 0) { - - mkdir_p("/run/systemd/journal", 0755); - - watch_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + watch_fd = watch_run_systemd_journal(IN_MOVED_TO|IN_DONT_FOLLOW|IN_ONLYDIR); if (watch_fd < 0) - return log_error_errno(errno, "Failed to create inotify watch: %m"); - - r = inotify_add_watch(watch_fd, "/run/systemd/journal", IN_MOVED_TO|IN_DONT_FOLLOW|IN_ONLYDIR); - if (r < 0) - return log_error_errno(errno, "Failed to watch journal directory: %m"); + return watch_fd; /* Recheck the flag file immediately, so that we don't miss any event since the last check. */ continue; } - /* OK, all preparatory steps done, let's wait until - * inotify reports an event. */ + /* OK, all preparatory steps done, let's wait until inotify reports an event. */ r = fd_wait_for_event(watch_fd, POLLIN, USEC_INFINITY); if (r < 0) @@ -2077,6 +2094,7 @@ static int wait_for_change(sd_journal *j, int poll_fd) { int main(int argc, char *argv[]) { bool previous_boot_id_valid = false, first_line = true, ellipsized = false, need_seek = false; + bool use_cursor = false, after_cursor = false; _cleanup_(sd_journal_closep) sd_journal *j = NULL; sd_id128_t previous_boot_id; int n_shown = 0, r, poll_fd = -1; @@ -2420,19 +2438,41 @@ int main(int argc, char *argv[]) { } } - if (arg_cursor || arg_after_cursor) { - r = sd_journal_seek_cursor(j, arg_cursor ?: arg_after_cursor); - if (r < 0) { - log_error_errno(r, "Failed to seek to cursor: %m"); - goto finish; + if (arg_cursor || arg_after_cursor || arg_cursor_file) { + _cleanup_free_ char *cursor_from_file = NULL; + const char *cursor = arg_cursor ?: arg_after_cursor; + + if (arg_cursor_file) { + r = read_one_line_file(arg_cursor_file, &cursor_from_file); + if (r < 0 && r != -ENOENT) { + log_error_errno(r, "Failed to read cursor file %s: %m", arg_cursor_file); + goto finish; + } + + if (r > 0) { + cursor = cursor_from_file; + after_cursor = true; + } + } else + after_cursor = !!arg_after_cursor; + + if (cursor) { + r = sd_journal_seek_cursor(j, cursor); + if (r < 0) { + log_error_errno(r, "Failed to seek to cursor: %m"); + goto finish; + } + use_cursor = true; } + } + if (use_cursor) { if (!arg_reverse) - r = sd_journal_next_skip(j, 1 + !!arg_after_cursor); + r = sd_journal_next_skip(j, 1 + after_cursor); else - r = sd_journal_previous_skip(j, 1 + !!arg_after_cursor); + r = sd_journal_previous_skip(j, 1 + after_cursor); - if (arg_after_cursor && r < 2) { + if (after_cursor && r < 2) { /* We couldn't find the next entry after the cursor. */ if (arg_follow) need_seek = true; @@ -2661,14 +2701,26 @@ int main(int argc, char *argv[]) { if (n_shown == 0 && !arg_quiet) printf("-- No entries --\n"); - if (arg_show_cursor) { + if (arg_show_cursor || arg_cursor_file) { _cleanup_free_ char *cursor = NULL; r = sd_journal_get_cursor(j, &cursor); if (r < 0 && r != -EADDRNOTAVAIL) log_error_errno(r, "Failed to get cursor: %m"); - else if (r >= 0) - printf("-- cursor: %s\n", cursor); + else if (r >= 0) { + if (arg_show_cursor) + printf("-- cursor: %s\n", cursor); + + if (arg_cursor_file) { + r = write_string_file(arg_cursor_file, cursor, + WRITE_STRING_FILE_CREATE | + WRITE_STRING_FILE_ATOMIC); + if (r < 0) + log_error_errno(r, + "Failed to write new cursor to %s: %m", + arg_cursor_file); + } + } } break; diff --git a/src/journal/journald-context.h b/src/journal/journald-context.h index 5e19c71f1..46f79fb5f 100644 --- a/src/journal/journald-context.h +++ b/src/journal/journald-context.h @@ -2,10 +2,13 @@ #pragma once #include +#include #include #include "sd-id128.h" +#include "time-util.h" + typedef struct ClientContext ClientContext; #include "journald-server.h" diff --git a/src/journal/journald-native.c b/src/journal/journald-native.c index 221188db1..4efe34c66 100644 --- a/src/journal/journald-native.c +++ b/src/journal/journald-native.c @@ -19,6 +19,7 @@ #include "journald-syslog.h" #include "journald-wall.h" #include "memfd-util.h" +#include "memory-util.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" diff --git a/src/journal/journald-rate-limit.c b/src/journal/journald-rate-limit.c index 0b42d5376..39e968bce 100644 --- a/src/journal/journald-rate-limit.c +++ b/src/journal/journald-rate-limit.c @@ -9,7 +9,7 @@ #include "list.h" #include "random-util.h" #include "string-util.h" -#include "util.h" +#include "time-util.h" #define POOLS_MAX 5 #define BUCKETS_MAX 127 diff --git a/src/journal/journald-rate-limit.h b/src/journal/journald-rate-limit.h index a2992800f..9dab51f7c 100644 --- a/src/journal/journald-rate-limit.h +++ b/src/journal/journald-rate-limit.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include "util.h" +#include "time-util.h" typedef struct JournalRateLimit JournalRateLimit; diff --git a/src/journal/journald-server.h b/src/journal/journald-server.h index 3f6b42ddd..5f0b3dd4a 100644 --- a/src/journal/journald-server.h +++ b/src/journal/journald-server.h @@ -16,6 +16,7 @@ typedef struct Server Server; #include "journald-stream.h" #include "list.h" #include "prioq.h" +#include "time-util.h" typedef enum Storage { STORAGE_AUTO, diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c index 137c8f044..24d4ac30b 100644 --- a/src/journal/journald-stream.c +++ b/src/journal/journald-stream.c @@ -13,6 +13,7 @@ #include "alloc-util.h" #include "dirent-util.h" #include "env-file.h" +#include "errno-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" @@ -159,7 +160,7 @@ static int stdout_stream_save(StdoutStream *s) { return log_oom(); } - mkdir_p("/run/systemd/journal/streams", 0755); + (void) mkdir_p("/run/systemd/journal/streams", 0755); r = fopen_temporary(s->state_file, &f, &temp_path); if (r < 0) @@ -605,7 +606,7 @@ static int stdout_stream_new(sd_event_source *es, int listen_fd, uint32_t revent fd = accept4(s->stdout_fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (fd < 0) { - if (errno == EAGAIN) + if (ERRNO_IS_ACCEPT_AGAIN(errno)) return 0; return log_error_errno(errno, "Failed to accept stdout connection: %m"); diff --git a/src/journal/journald-wall.h b/src/journal/journald-wall.h index b73059af4..026649eb7 100644 --- a/src/journal/journald-wall.h +++ b/src/journal/journald-wall.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include + #include "journald-server.h" void server_forward_wall(Server *s, int priority, const char *identifier, const char *message, const struct ucred *ucred); diff --git a/src/journal/mmap-cache.c b/src/journal/mmap-cache.c index 0dc453e70..e2f7bd7ec 100644 --- a/src/journal/mmap-cache.c +++ b/src/journal/mmap-cache.c @@ -5,14 +5,15 @@ #include #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "hashmap.h" #include "list.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "mmap-cache.h" #include "sigbus.h" -#include "util.h" typedef struct Window Window; typedef struct Context Context; diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 0f996283c..82e7d59fb 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -32,6 +32,7 @@ #include "list.h" #include "lookup3.h" #include "missing.h" +#include "nulstr-util.h" #include "path-util.h" #include "process-util.h" #include "replace-var.h" diff --git a/src/journal/test-compress-benchmark.c b/src/journal/test-compress-benchmark.c index 7f13b611e..100599705 100644 --- a/src/journal/test-compress-benchmark.c +++ b/src/journal/test-compress-benchmark.c @@ -4,12 +4,13 @@ #include "compress.h" #include "env-util.h" #include "macro.h" +#include "memory-util.h" +#include "nulstr-util.h" #include "parse-util.h" #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, size_t dst_alloc_size, size_t *dst_size); diff --git a/src/journal/test-compress.c b/src/journal/test-compress.c index 1b050b705..fac2b43c4 100644 --- a/src/journal/test-compress.c +++ b/src/journal/test-compress.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #if HAVE_LZ4 #include #endif @@ -9,11 +11,11 @@ #include "fd-util.h" #include "fs-util.h" #include "macro.h" +#include "memory-util.h" #include "path-util.h" #include "random-util.h" #include "tests.h" #include "tmpfile-util.h" -#include "util.h" #if HAVE_XZ # define XZ_OK 0 diff --git a/src/journal/test-journal-flush.c b/src/journal/test-journal-flush.c index 81dbc22a7..de9d23a00 100644 --- a/src/journal/test-journal-flush.c +++ b/src/journal/test-journal-flush.c @@ -1,10 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "sd-journal.h" #include "alloc-util.h" +#include "chattr-util.h" #include "journal-file.h" #include "journal-internal.h" #include "macro.h" @@ -19,6 +21,8 @@ int main(int argc, char *argv[]) { int r; assert_se(mkdtemp(dn)); + (void) chattr_path(dn, FS_NOCOW_FL, FS_NOCOW_FL, NULL); + fn = strappend(dn, "/test.journal"); r = journal_file_open(-1, fn, O_CREAT|O_RDWR, 0644, false, 0, false, NULL, NULL, NULL, NULL, &new_journal); diff --git a/src/journal/test-journal-init.c b/src/journal/test-journal-init.c index 860baca38..d10e61080 100644 --- a/src/journal/test-journal-init.c +++ b/src/journal/test-journal-init.c @@ -2,6 +2,7 @@ #include "sd-journal.h" +#include "chattr-util.h" #include "log.h" #include "parse-util.h" #include "rm-rf.h" @@ -11,7 +12,7 @@ int main(int argc, char *argv[]) { sd_journal *j; int r, i, I = 100; - char t[] = "/tmp/journal-stream-XXXXXX"; + char t[] = "/var/tmp/journal-stream-XXXXXX"; test_setup_logging(LOG_DEBUG); @@ -24,6 +25,7 @@ int main(int argc, char *argv[]) { log_info("Running %d loops", I); assert_se(mkdtemp(t)); + (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL); for (i = 0; i < I; i++) { r = sd_journal_open(&j, SD_JOURNAL_LOCAL_ONLY); diff --git a/src/journal/test-journal-interleaving.c b/src/journal/test-journal-interleaving.c index cf0561df6..449ac8ea1 100644 --- a/src/journal/test-journal-interleaving.c +++ b/src/journal/test-journal-interleaving.c @@ -6,6 +6,7 @@ #include "sd-journal.h" #include "alloc-util.h" +#include "chattr-util.h" #include "io-util.h" #include "journal-file.h" #include "journal-vacuum.h" @@ -130,13 +131,21 @@ static void setup_interleaved(void) { test_close(two); } +static void mkdtemp_chdir_chattr(char *path) { + assert_se(mkdtemp(path)); + assert_se(chdir(path) >= 0); + + /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our + * directory during the test run */ + (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL); +} + static void test_skip(void (*setup)(void)) { - char t[] = "/tmp/journal-skip-XXXXXX"; + char t[] = "/var/tmp/journal-skip-XXXXXX"; sd_journal *j; int r; - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); + mkdtemp_chdir_chattr(t); setup(); @@ -189,13 +198,12 @@ static void test_skip(void (*setup)(void)) { static void test_sequence_numbers(void) { - char t[] = "/tmp/journal-seq-XXXXXX"; + char t[] = "/var/tmp/journal-seq-XXXXXX"; JournalFile *one, *two; uint64_t seqnum = 0; sd_id128_t seqnum_id; - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); + mkdtemp_chdir_chattr(t); assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0644, true, (uint64_t) -1, false, NULL, NULL, NULL, NULL, &one) == 0); diff --git a/src/journal/test-journal-stream.c b/src/journal/test-journal-stream.c index 226c30f80..6d97bc5ce 100644 --- a/src/journal/test-journal-stream.c +++ b/src/journal/test-journal-stream.c @@ -6,6 +6,7 @@ #include "sd-journal.h" #include "alloc-util.h" +#include "chattr-util.h" #include "journal-file.h" #include "journal-internal.h" #include "log.h" @@ -59,7 +60,7 @@ static void verify_contents(sd_journal *j, unsigned skip) { int main(int argc, char *argv[]) { JournalFile *one, *two, *three; - char t[] = "/tmp/journal-stream-XXXXXX"; + char t[] = "/var/tmp/journal-stream-XXXXXX"; unsigned i; _cleanup_(sd_journal_closep) sd_journal *j = NULL; char *z; @@ -75,6 +76,7 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); + (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL); assert_se(journal_file_open(-1, "one.journal", O_RDWR|O_CREAT, 0666, true, (uint64_t) -1, false, NULL, NULL, NULL, NULL, &one) == 0); assert_se(journal_file_open(-1, "two.journal", O_RDWR|O_CREAT, 0666, true, (uint64_t) -1, false, NULL, NULL, NULL, NULL, &two) == 0); diff --git a/src/journal/test-journal-verify.c b/src/journal/test-journal-verify.c index c4fa41e07..2893a7c00 100644 --- a/src/journal/test-journal-verify.c +++ b/src/journal/test-journal-verify.c @@ -4,6 +4,7 @@ #include #include +#include "chattr-util.h" #include "fd-util.h" #include "io-util.h" #include "journal-file.h" @@ -51,7 +52,7 @@ static int raw_verify(const char *fn, const char *verification_key) { } int main(int argc, char *argv[]) { - char t[] = "/tmp/journal-XXXXXX"; + char t[] = "/var/tmp/journal-XXXXXX"; unsigned n; JournalFile *f; const char *verification_key = argv[1]; @@ -70,6 +71,7 @@ int main(int argc, char *argv[]) { assert_se(mkdtemp(t)); assert_se(chdir(t) >= 0); + (void) chattr_path(t, FS_NOCOW_FL, FS_NOCOW_FL, NULL); log_info("Generating..."); diff --git a/src/journal/test-journal.c b/src/journal/test-journal.c index 0795e0da0..7f56668af 100644 --- a/src/journal/test-journal.c +++ b/src/journal/test-journal.c @@ -3,6 +3,7 @@ #include #include +#include "chattr-util.h" #include "io-util.h" #include "journal-authenticate.h" #include "journal-file.h" @@ -13,6 +14,15 @@ static bool arg_keep = false; +static void mkdtemp_chdir_chattr(char *path) { + assert_se(mkdtemp(path)); + assert_se(chdir(path) >= 0); + + /* Speed up things a bit on btrfs, ensuring that CoW is turned off for all files created in our + * directory during the test run */ + (void) chattr_path(path, FS_NOCOW_FL, FS_NOCOW_FL, NULL); +} + static void test_non_empty(void) { dual_timestamp ts; JournalFile *f; @@ -21,12 +31,11 @@ static void test_non_empty(void) { Object *o; uint64_t p; sd_id128_t fake_boot_id; - char t[] = "/tmp/journal-XXXXXX"; + char t[] = "/var/tmp/journal-XXXXXX"; test_setup_logging(LOG_DEBUG); - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); + mkdtemp_chdir_chattr(t); assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, (uint64_t) -1, true, NULL, NULL, NULL, NULL, &f) == 0); @@ -109,12 +118,11 @@ static void test_non_empty(void) { static void test_empty(void) { JournalFile *f1, *f2, *f3, *f4; - char t[] = "/tmp/journal-XXXXXX"; + char t[] = "/var/tmp/journal-XXXXXX"; test_setup_logging(LOG_DEBUG); - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); + mkdtemp_chdir_chattr(t); assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, false, (uint64_t) -1, false, NULL, NULL, NULL, NULL, &f1) == 0); @@ -156,7 +164,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { struct iovec iovec; Object *o; uint64_t p; - char t[] = "/tmp/journal-XXXXXX"; + char t[] = "/var/tmp/journal-XXXXXX"; char data[2048] = {0}; bool is_compressed; int r; @@ -165,8 +173,7 @@ static bool check_compressed(uint64_t compress_threshold, uint64_t data_size) { test_setup_logging(LOG_DEBUG); - assert_se(mkdtemp(t)); - assert_se(chdir(t) >= 0); + mkdtemp_chdir_chattr(t); assert_se(journal_file_open(-1, "test.journal", O_RDWR|O_CREAT, 0666, true, compress_threshold, true, NULL, NULL, NULL, NULL, &f) == 0); diff --git a/src/kernel-install/00-entry-directory.install b/src/kernel-install/00-entry-directory.install new file mode 100644 index 000000000..2aa8c5831 --- /dev/null +++ b/src/kernel-install/00-entry-directory.install @@ -0,0 +1,32 @@ +#!/bin/bash +# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- +# ex: ts=8 sw=4 sts=4 et filetype=sh + +COMMAND="$1" +KERNEL_VERSION="$2" +ENTRY_DIR_ABS="$3" +KERNEL_IMAGE="$4" +INITRD_OPTIONS_START="5" + +if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then + exit 0 +fi + +if [[ $COMMAND != add ]]; then + exit 0 +fi + +# If the boot dir exists (e.g. $ESP/), +# create the entry directory ($ESP//). +# This is the only function of this plugin. +MACHINE_ID_DIR="${ENTRY_DIR_ABS%/*}" +if ! [ -d "$MACHINE_ID_DIR" ]; then + exit 0 +fi + +if [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ]; then + echo "+mkdir -v -p $ENTRY_DIR_ABS" + exec mkdir -v -p "$ENTRY_DIR_ABS" +else + exec mkdir -p "$ENTRY_DIR_ABS" +fi diff --git a/src/kernel-install/50-depmod.install b/src/kernel-install/50-depmod.install index 88f550a48..268b6ce0a 100644 --- a/src/kernel-install/50-depmod.install +++ b/src/kernel-install/50-depmod.install @@ -2,15 +2,25 @@ # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh -[[ $2 ]] || exit 1 +COMMAND="$1" +KERNEL_VERSION="$2" +ENTRY_DIR_ABS="$3" +KERNEL_IMAGE="$4" +INITRD_OPTIONS_START="5" -case "$1" in +[[ $KERNEL_VERSION ]] || exit 1 + +case "$COMMAND" in add) - [[ -d /lib/modules/"$2"/kernel ]] || exit 0 - exec depmod -a "$2" + [[ -d "/lib/modules/${KERNEL_VERSION}/kernel" ]] || exit 0 + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "Running depmod -a ${KERNEL_VERSION}" + exec depmod -a "${KERNEL_VERSION}" ;; remove) - exec rm -f /lib/modules/"$2"/modules.{alias{,.bin},builtin.bin,dep{,.bin},devname,softdep,symbols{,.bin}} + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "Removing /lib/modules/${KERNEL_VERSION}/modules.dep and associated files" + exec rm -f /lib/modules/"${KERNEL_VERSION}"/modules.{alias{,.bin},builtin.bin,dep{,.bin},devname,softdep,symbols{,.bin}} ;; *) exit 0 diff --git a/src/kernel-install/90-loaderentry.install b/src/kernel-install/90-loaderentry.install index 75dd5a1b7..2fb0f5207 100644 --- a/src/kernel-install/90-loaderentry.install +++ b/src/kernel-install/90-loaderentry.install @@ -4,7 +4,7 @@ COMMAND="$1" KERNEL_VERSION="$2" -BOOT_DIR_ABS="$3" +ENTRY_DIR_ABS="$3" KERNEL_IMAGE="$4" INITRD_OPTIONS_START="5" @@ -12,14 +12,14 @@ if ! [[ $KERNEL_INSTALL_MACHINE_ID ]]; then exit 0 fi -if ! [[ -d "$BOOT_DIR_ABS" ]]; then +if ! [[ -d "$ENTRY_DIR_ABS" ]]; then exit 0 fi MACHINE_ID=$KERNEL_INSTALL_MACHINE_ID -BOOT_DIR="/$MACHINE_ID/$KERNEL_VERSION" -BOOT_ROOT=${BOOT_DIR_ABS%$BOOT_DIR} +ENTRY_DIR="/$MACHINE_ID/$KERNEL_VERSION" +BOOT_ROOT=${ENTRY_DIR_ABS%$ENTRY_DIR} if [[ $COMMAND == remove ]]; then rm -f "$BOOT_ROOT/loader/entries/$MACHINE_ID-$KERNEL_VERSION.conf" @@ -76,10 +76,10 @@ 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" || { - echo "Could not copy '$KERNEL_IMAGE to '$BOOT_DIR_ABS/linux'." >&2 +cp "$KERNEL_IMAGE" "$ENTRY_DIR_ABS/linux" && + chown root:root "$ENTRY_DIR_ABS/linux" && + chmod 0644 "$ENTRY_DIR_ABS/linux" || { + echo "Could not copy '$KERNEL_IMAGE to '$ENTRY_DIR_ABS/linux'." >&2 exit 1 } @@ -88,10 +88,12 @@ INITRD_OPTIONS=( "${@:${INITRD_OPTIONS_START}}" ) for initrd in "${INITRD_OPTIONS[@]}"; do if [[ -f "${initrd}" ]]; then initrd_basename="$(basename ${initrd})" - cp "${initrd}" "$BOOT_DIR_ABS/${initrd_basename}" && - chown root:root "$BOOT_DIR_ABS/${initrd_basename}" && - chmod 0644 "$BOOT_DIR_ABS/${initrd_basename}" || { - echo "Could not copy '${initrd}' to '$BOOT_DIR_ABS/${initrd_basename}'." >&2 + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "Installing $ENTRY_DIR_ABS/${initrd_basename}" + cp "${initrd}" "$ENTRY_DIR_ABS/${initrd_basename}" && + chown root:root "$ENTRY_DIR_ABS/${initrd_basename}" && + chmod 0644 "$ENTRY_DIR_ABS/${initrd_basename}" || { + echo "Could not copy '${initrd}' to '$ENTRY_DIR_ABS/${initrd_basename}'." >&2 exit 1 } fi @@ -106,15 +108,17 @@ mkdir -p "${LOADER_ENTRY%/*}" || { exit 1 } +[ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "Creating $LOADER_ENTRY" { echo "title $PRETTY_NAME" echo "version $KERNEL_VERSION" echo "machine-id $MACHINE_ID" echo "options ${BOOT_OPTIONS[*]}" - echo "linux $BOOT_DIR/linux" + echo "linux $ENTRY_DIR/linux" for initrd in "${INITRD_OPTIONS[@]}"; do - [[ -f $BOOT_DIR_ABS/$(basename ${initrd}) ]] && \ - echo "initrd $BOOT_DIR/$(basename ${initrd})" + [[ -f $ENTRY_DIR_ABS/$(basename ${initrd}) ]] && \ + echo "initrd $ENTRY_DIR/$(basename ${initrd})" done : } > "$LOADER_ENTRY" || { diff --git a/src/kernel-install/kernel-install b/src/kernel-install/kernel-install index b85c7c557..e7f2beb57 100644 --- a/src/kernel-install/kernel-install +++ b/src/kernel-install/kernel-install @@ -5,7 +5,6 @@ # # 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 @@ -63,6 +62,13 @@ for i in "$@"; do fi done +KERNEL_INSTALL_VERBOSE=0 +if [ "$1" == "--verbose" -o "$1" == "-v" ]; then + shift + KERNEL_INSTALL_VERBOSE=1 +fi +export KERNEL_INSTALL_VERBOSE + if [[ "${0##*/}" == 'installkernel' ]]; then COMMAND='add' # make install doesn't pass any parameter wrt initrd handling @@ -86,20 +92,20 @@ if [[ ! $COMMAND ]] || [[ ! $KERNEL_VERSION ]]; then fi if ! [[ $MACHINE_ID ]]; then - BOOT_DIR_ABS=$(mktemp -d /tmp/kernel-install.XXXXX) || exit 1 - trap "rm -rf '$BOOT_DIR_ABS'" EXIT INT QUIT PIPE + ENTRY_DIR_ABS=$(mktemp -d /tmp/kernel-install.XXXXX) || exit 1 + trap "rm -rf '$ENTRY_DIR_ABS'" EXIT INT QUIT PIPE elif [[ -d /efi/loader/entries ]] || [[ -d /efi/$MACHINE_ID ]]; then - BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" + ENTRY_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" elif [[ -d /boot/loader/entries ]] || [[ -d /boot/$MACHINE_ID ]]; then - BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" + ENTRY_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" elif [[ -d /boot/efi/loader/entries ]] || [[ -d /boot/efi/$MACHINE_ID ]]; then - BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" + ENTRY_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" elif mountpoint -q /efi; then - BOOT_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" + ENTRY_DIR_ABS="/efi/$MACHINE_ID/$KERNEL_VERSION" elif mountpoint -q /boot/efi; then - BOOT_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" + ENTRY_DIR_ABS="/boot/efi/$MACHINE_ID/$KERNEL_VERSION" else - BOOT_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" + ENTRY_DIR_ABS="/boot/$MACHINE_ID/$KERNEL_VERSION" fi export KERNEL_INSTALL_MACHINE_ID=$MACHINE_ID @@ -119,14 +125,16 @@ case $COMMAND in exit 1 fi - mkdir -p "$BOOT_DIR_ABS" || { - echo "Could not create boot directory '$BOOT_DIR_ABS'." >&2 + if [[ ! -f "$KERNEL_IMAGE" ]]; then + echo "Kernel image argument ${KERNEL_IMAGE} not a file" >&2 exit 1 - } + fi for f in "${PLUGINS[@]}"; do if [[ -x $f ]]; then - "$f" add "$KERNEL_VERSION" "$BOOT_DIR_ABS" "$KERNEL_IMAGE" "${INITRD_OPTIONS[@]}" + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "+$f add $KERNEL_VERSION $ENTRY_DIR_ABS $KERNEL_IMAGE ${INITRD_OPTIONS[@]}" + "$f" add "$KERNEL_VERSION" "$ENTRY_DIR_ABS" "$KERNEL_IMAGE" "${INITRD_OPTIONS[@]}" x=$? if [[ $x == $SKIP_REMAINING ]]; then ret=0 @@ -136,11 +144,11 @@ case $COMMAND in fi done - if ! [[ $MACHINE_ID ]] && ! rmdir "$BOOT_DIR_ABS"; then - echo "Warning: In kernel-install plugins, requiring BOOT_DIR_ABS to be preset is deprecated." >&2 - echo " All plugins should not put anything in BOOT_DIR_ABS if the environment" >&2 + if ! [[ $MACHINE_ID ]] && ! rmdir "$ENTRY_DIR_ABS"; then + echo "Warning: In kernel-install plugins, requiring ENTRY_DIR_ABS to be preset is deprecated." >&2 + echo " All plugins should not put anything in ENTRY_DIR_ABS if the environment" >&2 echo " variable KERNEL_INSTALL_MACHINE_ID is empty." >&2 - rm -rf "$BOOT_DIR_ABS" + rm -rf "$ENTRY_DIR_ABS" ((ret+=$?)) fi ;; @@ -148,7 +156,9 @@ case $COMMAND in remove) for f in "${PLUGINS[@]}"; do if [[ -x $f ]]; then - "$f" remove "$KERNEL_VERSION" "$BOOT_DIR_ABS" + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "+$f remove $KERNEL_VERSION $ENTRY_DIR_ABS" + "$f" remove "$KERNEL_VERSION" "$ENTRY_DIR_ABS" x=$? if [[ $x == $SKIP_REMAINING ]]; then ret=0 @@ -158,7 +168,10 @@ case $COMMAND in fi done - rm -rf "$BOOT_DIR_ABS" + [ "$KERNEL_INSTALL_VERBOSE" -gt 0 ] && \ + echo "Removing $ENTRY_DIR_ABS" + + rm -rf "$ENTRY_DIR_ABS" ((ret+=$?)) ;; diff --git a/src/kernel-install/meson.build b/src/kernel-install/meson.build index c6e6f816d..261c3aaae 100644 --- a/src/kernel-install/meson.build +++ b/src/kernel-install/meson.build @@ -4,7 +4,8 @@ install_data('kernel-install', install_mode : 'rwxr-xr-x', install_dir : bindir) -install_data('50-depmod.install', +install_data('00-entry-directory.install', + '50-depmod.install', '90-loaderentry.install', install_mode : 'rwxr-xr-x', install_dir : kernelinstalldir) diff --git a/src/libsystemd-network/dhcp-identifier.c b/src/libsystemd-network/dhcp-identifier.c index 04bf64cce..0c1ff34e5 100644 --- a/src/libsystemd-network/dhcp-identifier.c +++ b/src/libsystemd-network/dhcp-identifier.c @@ -12,6 +12,7 @@ #include "siphash24.h" #include "sparse-endian.h" #include "stdio-util.h" +#include "udev-util.h" #include "virt.h" #define SYSTEMD_PEN 43793 @@ -182,6 +183,13 @@ int dhcp_identifier_set_iaid( /* not yet ready */ return -EBUSY; + r = device_is_renaming(device); + if (r < 0) + return r; + if (r > 0) + /* device is under renaming */ + return -EBUSY; + name = net_get_name(device); } } diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 9d245a905..122042ab5 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -41,7 +41,6 @@ struct sd_dhcp_lease { /* each 0 if unset */ be32_t address; be32_t server_address; - be32_t router; be32_t next_server; bool have_subnet_mask; @@ -50,6 +49,9 @@ struct sd_dhcp_lease { bool have_broadcast; be32_t broadcast; + struct in_addr *router; + size_t router_size; + struct in_addr *dns; size_t dns_size; diff --git a/src/libsystemd-network/dhcp-network.c b/src/libsystemd-network/dhcp-network.c index 0e5b4147a..94c10ed14 100644 --- a/src/libsystemd-network/dhcp-network.c +++ b/src/libsystemd-network/dhcp-network.c @@ -50,12 +50,16 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.htype)), /* A <- DHCP header type */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arp_type, 1, 0), /* header type == arp_type ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ - BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */ - BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ - BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.xid)), /* A <- client identifier */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, xid, 1, 0), /* client identifier == xid ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_B + BPF_ABS, offsetof(DHCPPacket, dhcp.hlen)), /* A <- MAC address length */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, dhcp_hlen, 1, 0), /* address length == dhcp_hlen ? */ + BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + + /* We only support MAC address length to be either 0 or 6 (ETH_ALEN). Optionally + * compare chaddr for ETH_ALEN bytes. */ + BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, ETH_ALEN, 0, 12), /* A (the MAC address length) == ETH_ALEN ? */ BPF_STMT(BPF_LD + BPF_IMM, unaligned_read_be32(ð_mac->ether_addr_octet[0])), /* A <- 4 bytes of client's MAC */ BPF_STMT(BPF_MISC + BPF_TAX, 0), /* X <- A */ BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.chaddr)), /* A <- 4 bytes of MAC from dhcp.chaddr */ @@ -68,6 +72,7 @@ static int _bind_raw_socket(int ifindex, union sockaddr_union *link, BPF_STMT(BPF_ALU + BPF_XOR + BPF_X, 0), /* A xor X */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, 0, 1, 0), /* A == 0 ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ + BPF_STMT(BPF_LD + BPF_W + BPF_ABS, offsetof(DHCPPacket, dhcp.magic)), /* A <- DHCP magic cookie */ BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, DHCP_MAGIC_COOKIE, 1, 0), /* cookie == DHCP magic cookie ? */ BPF_STMT(BPF_RET + BPF_K, 0), /* ignore */ @@ -148,7 +153,6 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { .in.sin_addr.s_addr = address, }; _cleanup_close_ int s = -1; - char ifname[IF_NAMESIZE] = ""; int r; s = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); @@ -164,12 +168,9 @@ int dhcp_network_bind_udp_socket(int ifindex, be32_t address, uint16_t port) { return r; if (ifindex > 0) { - if (if_indextoname(ifindex, ifname) == 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + r = socket_bind_to_ifindex(s, ifindex); if (r < 0) - return -errno; + return r; } if (address == INADDR_ANY) { diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index ad3f92546..a68de4ff9 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -9,10 +9,10 @@ #include #include "alloc-util.h" -#include "utf8.h" -#include "strv.h" - #include "dhcp-internal.h" +#include "memory-util.h" +#include "strv.h" +#include "utf8.h" static int option_append(uint8_t options[], size_t size, size_t *offset, uint8_t code, size_t optlen, const void *optval) { diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 8a7c5bc28..b30be7e5b 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -11,7 +11,7 @@ #include "dhcp-internal.h" #include "hashmap.h" #include "log.h" -#include "util.h" +#include "time-util.h" typedef struct DHCPClientId { size_t length; diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index a2aac9a79..017402c53 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -14,10 +14,10 @@ #include "dhcp6-lease-internal.h" #include "dhcp6-protocol.h" #include "dns-domain.h" +#include "memory-util.h" #include "sparse-endian.h" #include "strv.h" #include "unaligned.h" -#include "util.h" typedef struct DHCP6StatusOption { struct DHCP6Option option; diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c index e535b12cd..e1f193ab2 100644 --- a/src/libsystemd-network/icmp6-util.c +++ b/src/libsystemd-network/icmp6-util.c @@ -31,9 +31,8 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter, const struct ipv6_mreq *mreq) { - int index = mreq->ipv6mr_interface; + int ifindex = mreq->ipv6mr_interface; _cleanup_close_ int s = -1; - char ifname[IF_NAMESIZE] = ""; int r; s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6); @@ -52,7 +51,7 @@ 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_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, index); + r = setsockopt_int(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, ifindex); if (r < 0) return r; @@ -76,12 +75,9 @@ static int icmp6_bind_router_message(const struct icmp6_filter *filter, if (r < 0) return r; - if (if_indextoname(index, ifname) == 0) - return -errno; - - r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE, ifname, strlen(ifname)); + r = socket_bind_to_ifindex(s, ifindex); if (r < 0) - return -errno; + return r; return TAKE_FD(s); } diff --git a/src/libsystemd-network/lldp-neighbor.c b/src/libsystemd-network/lldp-neighbor.c index f6db62594..9bae4a3c6 100644 --- a/src/libsystemd-network/lldp-neighbor.c +++ b/src/libsystemd-network/lldp-neighbor.c @@ -7,9 +7,9 @@ #include "in-addr-util.h" #include "lldp-internal.h" #include "lldp-neighbor.h" +#include "memory-util.h" #include "missing.h" #include "unaligned.h" -#include "util.h" static void lldp_neighbor_id_hash_func(const LLDPNeighborID *id, struct siphash *state) { siphash24_compress(id->chassis_id, id->chassis_id_size, state); diff --git a/src/libsystemd-network/ndisc-internal.h b/src/libsystemd-network/ndisc-internal.h index 0c04fea8e..28f801cd7 100644 --- a/src/libsystemd-network/ndisc-internal.h +++ b/src/libsystemd-network/ndisc-internal.h @@ -6,6 +6,7 @@ ***/ #include "log.h" +#include "time-util.h" #include "sd-ndisc.h" diff --git a/src/libsystemd-network/ndisc-router.c b/src/libsystemd-network/ndisc-router.c index 6935311b9..c0938724e 100644 --- a/src/libsystemd-network/ndisc-router.c +++ b/src/libsystemd-network/ndisc-router.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "dns-domain.h" #include "hostname-util.h" +#include "memory-util.h" #include "missing.h" #include "ndisc-internal.h" #include "ndisc-router.h" diff --git a/src/libsystemd-network/network-internal.c b/src/libsystemd-network/network-internal.c index 0348e7fa9..2154cf7ea 100644 --- a/src/libsystemd-network/network-internal.c +++ b/src/libsystemd-network/network-internal.c @@ -10,6 +10,7 @@ #include "alloc-util.h" #include "condition.h" #include "conf-parser.h" +#include "device-util.h" #include "dhcp-lease-internal.h" #include "ether-addr-util.h" #include "hexdecoct.h" @@ -40,31 +41,35 @@ const char *net_get_name(sd_device *device) { int net_get_unique_predictable_data(sd_device *device, uint64_t *result) { size_t l, sz = 0; - const char *name = NULL; + const char *name; int r; uint8_t *v; assert(device); + /* net_get_name() will return one of the device names based on stable information about the + * device. If this is not available, we fall back to using the device name. */ name = net_get_name(device); if (!name) - return -ENOENT; + (void) sd_device_get_sysname(device, &name); + if (!name) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA), + "No stable identifying information found"); + log_device_debug(device, "Using \"%s\" as stable identifying information", name); l = strlen(name); sz = sizeof(sd_id128_t) + l; v = newa(uint8_t, sz); - /* fetch some persistent data unique to this machine */ + /* Fetch some persistent data unique to this machine */ r = sd_id128_get_machine((sd_id128_t*) v); if (r < 0) return r; memcpy(v + sizeof(sd_id128_t), name, l); - /* Let's hash the machine ID plus the device name. We - * use a fixed, but originally randomly created hash - * key here. */ + /* Let's hash the machine ID plus the device name. We use + * a fixed, but originally randomly created hash key here. */ *result = htole64(siphash24(v, sz, HASH_KEY.bytes)); - return 0; } @@ -95,33 +100,12 @@ bool net_match_config(Set *match_mac, char * const *match_drivers, char * const *match_types, char * const *match_names, - Condition *match_host, - Condition *match_virt, - Condition *match_kernel_cmdline, - Condition *match_kernel_version, - Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, - const char *dev_parent_driver, const char *dev_driver, const char *dev_type, const char *dev_name) { - if (match_host && condition_test(match_host) <= 0) - return false; - - if (match_virt && condition_test(match_virt) <= 0) - return false; - - if (match_kernel_cmdline && condition_test(match_kernel_cmdline) <= 0) - return false; - - if (match_kernel_version && condition_test(match_kernel_version) <= 0) - return false; - - if (match_arch && condition_test(match_arch) <= 0) - return false; - if (match_mac && (!dev_mac || !set_contains(match_mac, dev_mac))) return false; @@ -152,32 +136,31 @@ int config_parse_net_condition(const char *unit, void *userdata) { ConditionType cond = ltype; - Condition **ret = data; + Condition **list = data, *c; bool negate; - Condition *c; - _cleanup_free_ char *s = NULL; assert(filename); assert(lvalue); assert(rvalue); assert(data); + if (isempty(rvalue)) { + *list = condition_free_list_type(*list, cond); + return 0; + } + negate = rvalue[0] == '!'; if (negate) rvalue++; - s = strdup(rvalue); - if (!s) - return log_oom(); - - c = condition_new(cond, s, false, negate); + c = condition_new(cond, rvalue, false, negate); if (!c) return log_oom(); - if (*ret) - condition_free(*ret); + /* Drop previous assignment. */ + *list = condition_free_list_type(*list, cond); - *ret = c; + LIST_PREPEND(conditions, *list, c); return 0; } @@ -409,16 +392,33 @@ int config_parse_bridge_port_priority( return 0; } -void serialize_in_addrs(FILE *f, const struct in_addr *addresses, size_t size) { - unsigned i; +size_t serialize_in_addrs(FILE *f, + const struct in_addr *addresses, + size_t size, + bool with_leading_space, + bool (*predicate)(const struct in_addr *addr)) { + size_t count; + size_t i; assert(f); assert(addresses); - assert(size); - for (i = 0; i < size; i++) - fprintf(f, "%s%s", inet_ntoa(addresses[i]), - (i < (size - 1)) ? " ": ""); + count = 0; + + for (i = 0; i < size; i++) { + char sbuf[INET_ADDRSTRLEN]; + + if (predicate && !predicate(&addresses[i])) + continue; + if (with_leading_space) + fputc(' ', f); + else + with_leading_space = true; + fputs(inet_ntop(AF_INET, &addresses[i], sbuf, sizeof(sbuf)), f); + count++; + } + + return count; } int deserialize_in_addrs(struct in_addr **ret, const char *string) { @@ -452,7 +452,7 @@ int deserialize_in_addrs(struct in_addr **ret, const char *string) { size++; } - *ret = TAKE_PTR(addresses); + *ret = size > 0 ? TAKE_PTR(addresses) : NULL; return size; } @@ -521,6 +521,7 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz fprintf(f, "%s=", key); for (i = 0; i < size; i++) { + char sbuf[INET_ADDRSTRLEN]; struct in_addr dest, gw; uint8_t length; @@ -528,8 +529,8 @@ void serialize_dhcp_routes(FILE *f, const char *key, sd_dhcp_route **routes, siz assert_se(sd_dhcp_route_get_gateway(routes[i], &gw) >= 0); assert_se(sd_dhcp_route_get_destination_prefix_length(routes[i], &length) >= 0); - fprintf(f, "%s/%" PRIu8, inet_ntoa(dest), length); - fprintf(f, ",%s%s", inet_ntoa(gw), (i < (size - 1)) ? " ": ""); + fprintf(f, "%s/%" PRIu8, inet_ntop(AF_INET, &dest, sbuf, sizeof(sbuf)), length); + fprintf(f, ",%s%s", inet_ntop(AF_INET, &gw, sbuf, sizeof(sbuf)), (i < (size - 1)) ? " ": ""); } fputs("\n", f); diff --git a/src/libsystemd-network/network-internal.h b/src/libsystemd-network/network-internal.h index 0c8da848c..62f5a4a76 100644 --- a/src/libsystemd-network/network-internal.h +++ b/src/libsystemd-network/network-internal.h @@ -6,9 +6,10 @@ #include "sd-device.h" #include "sd-dhcp-lease.h" -#include "condition.h" #include "conf-parser.h" +#include "def.h" #include "set.h" +#include "strv.h" #define LINK_BRIDGE_PORT_PRIORITY_INVALID 128 #define LINK_BRIDGE_PORT_PRIORITY_MAX 63 @@ -18,14 +19,8 @@ bool net_match_config(Set *match_mac, char * const *match_driver, char * const *match_type, char * const *match_name, - Condition *match_host, - Condition *match_virt, - Condition *match_kernel_cmdline, - Condition *match_kernel_version, - Condition *match_arch, const struct ether_addr *dev_mac, const char *dev_path, - const char *dev_parent_driver, const char *dev_driver, const char *dev_type, const char *dev_name); @@ -40,7 +35,11 @@ CONFIG_PARSER_PROTOTYPE(config_parse_bridge_port_priority); 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); +size_t serialize_in_addrs(FILE *f, + const struct in_addr *addresses, + size_t size, + bool with_leading_space, + bool (*predicate)(const struct in_addr *addr)); int deserialize_in_addrs(struct in_addr **addresses, const char *string); void serialize_in6_addrs(FILE *f, const struct in6_addr *addresses, size_t size); @@ -54,3 +53,5 @@ int deserialize_dhcp_routes(struct sd_dhcp_route **ret, size_t *ret_size, size_t /* It is not necessary to add deserialize_dhcp_option(). Use unhexmem() instead. */ int serialize_dhcp_option(FILE *f, const char *key, const void *data, size_t size); + +#define NETWORK_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/network")) diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 35fc88ef9..97e1dd370 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -24,10 +24,10 @@ #include "event-util.h" #include "hostname-util.h" #include "io-util.h" +#include "memory-util.h" #include "random-util.h" #include "string-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) @@ -1676,8 +1676,7 @@ static int client_receive_message_udp( sd_dhcp_client *client = userdata; _cleanup_free_ DHCPMessage *message = NULL; - const struct ether_addr zero_mac = {}; - const struct ether_addr *expected_chaddr = NULL; + const uint8_t *expected_chaddr = NULL; uint8_t expected_hlen = 0; ssize_t len, buflen; @@ -1685,6 +1684,12 @@ static int client_receive_message_udp( assert(client); buflen = next_datagram_size_fd(fd); + if (buflen == -ENETDOWN) { + /* the link is down. Don't return an error or the I/O event + source will be disconnected and we won't be able to receive + packets again when the link comes back. */ + return 0; + } if (buflen < 0) return buflen; @@ -1694,7 +1699,8 @@ static int client_receive_message_udp( len = recv(fd, message, buflen, 0); if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + /* see comment above for why we shouldn't error out on ENETDOWN. */ + if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) return 0; return log_dhcp_client_errno(client, errno, @@ -1722,11 +1728,7 @@ static int client_receive_message_udp( if (client->arp_type == ARPHRD_ETHER) { expected_hlen = ETH_ALEN; - expected_chaddr = (const struct ether_addr *) &client->mac_addr; - } else { - /* Non-Ethernet links expect zero chaddr */ - expected_hlen = 0; - expected_chaddr = &zero_mac; + expected_chaddr = &client->mac_addr[0]; } if (message->hlen != expected_hlen) { @@ -1734,7 +1736,7 @@ static int client_receive_message_udp( return 0; } - if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { + if (expected_hlen > 0 && memcmp(&message->chaddr[0], expected_chaddr, expected_hlen)) { log_dhcp_client(client, "Received chaddr does not match expected: ignoring"); return 0; } @@ -1776,6 +1778,8 @@ static int client_receive_message_raw( assert(client); buflen = next_datagram_size_fd(fd); + if (buflen == -ENETDOWN) + return 0; if (buflen < 0) return buflen; @@ -1787,7 +1791,7 @@ static int client_receive_message_raw( len = recvmsg(fd, &msg, 0); if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) return 0; return log_dhcp_client_errno(client, errno, diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 13badbf0b..a16314a9d 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -9,6 +9,9 @@ #include #include #include +#include +#include +#include #include "sd-dhcp-lease.h" @@ -151,15 +154,15 @@ int sd_dhcp_lease_get_root_path(sd_dhcp_lease *lease, const char **root_path) { return 0; } -int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr) { +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr) { assert_return(lease, -EINVAL); assert_return(addr, -EINVAL); - if (lease->router == 0) + if (lease->router_size <= 0) return -ENODATA; - addr->s_addr = lease->router; - return 0; + *addr = lease->router; + return (int) lease->router_size; } int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr) { @@ -261,6 +264,7 @@ static sd_dhcp_lease *dhcp_lease_free(sd_dhcp_lease *lease) { } free(lease->root_path); + free(lease->router); free(lease->timezone); free(lease->hostname); free(lease->domainname); @@ -370,23 +374,6 @@ static int lease_parse_domain(const uint8_t *option, size_t len, char **ret) { return 0; } -static void filter_bogus_addresses(struct in_addr *addresses, size_t *n) { - size_t i, j; - - /* Silently filter DNS/NTP servers supplied to us that do not make outside of the local scope. */ - - for (i = 0, j = 0; i < *n; i ++) { - - if (in4_addr_is_null(addresses+i) || - in4_addr_is_localhost(addresses+i)) - continue; - - addresses[j++] = addresses[i]; - } - - *n = j; -} - static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_addr **ret, size_t *n_ret) { assert(option); assert(ret); @@ -408,8 +395,6 @@ static int lease_parse_in_addrs(const uint8_t *option, size_t len, struct in_add if (!addresses) return -ENOMEM; - filter_bogus_addresses(addresses, &n_addresses); - free(*ret); *ret = addresses; *n_ret = n_addresses; @@ -554,11 +539,9 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; case SD_DHCP_OPTION_ROUTER: - if (len >= 4) { - r = lease_parse_be32(option, 4, &lease->router); - if (r < 0) - log_debug_errno(r, "Failed to parse router address, ignoring: %m"); - } + r = lease_parse_in_addrs(option, len, &lease->router, &lease->router_size); + if (r < 0) + log_debug_errno(r, "Failed to parse router addresses, ignoring: %m"); break; case SD_DHCP_OPTION_DOMAIN_NAME_SERVER: @@ -820,7 +803,6 @@ int dhcp_lease_new(sd_dhcp_lease **ret) { if (!lease) return -ENOMEM; - lease->router = INADDR_ANY; lease->n_ref = 1; *ret = lease; @@ -835,6 +817,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { const struct in_addr *addresses; const void *client_id, *data; size_t client_id_len, data_len; + char sbuf[INET_ADDRSTRLEN]; const char *string; uint16_t mtu; _cleanup_free_ sd_dhcp_route **routes = NULL; @@ -857,27 +840,30 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_address(lease, &address); if (r >= 0) - fprintf(f, "ADDRESS=%s\n", inet_ntoa(address)); + fprintf(f, "ADDRESS=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_netmask(lease, &address); if (r >= 0) - fprintf(f, "NETMASK=%s\n", inet_ntoa(address)); + fprintf(f, "NETMASK=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); - r = sd_dhcp_lease_get_router(lease, &address); - if (r >= 0) - fprintf(f, "ROUTER=%s\n", inet_ntoa(address)); + r = sd_dhcp_lease_get_router(lease, &addresses); + if (r > 0) { + fputs("ROUTER=", f); + serialize_in_addrs(f, addresses, r, false, NULL); + fputc('\n', f); + } r = sd_dhcp_lease_get_server_identifier(lease, &address); if (r >= 0) - fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntoa(address)); + fprintf(f, "SERVER_ADDRESS=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_next_server(lease, &address); if (r >= 0) - fprintf(f, "NEXT_SERVER=%s\n", inet_ntoa(address)); + fprintf(f, "NEXT_SERVER=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_broadcast(lease, &address); if (r >= 0) - fprintf(f, "BROADCAST=%s\n", inet_ntoa(address)); + fprintf(f, "BROADCAST=%s\n", inet_ntop(AF_INET, &address, sbuf, sizeof(sbuf))); r = sd_dhcp_lease_get_mtu(lease, &mtu); if (r >= 0) @@ -898,15 +884,15 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { r = sd_dhcp_lease_get_dns(lease, &addresses); if (r > 0) { fputs("DNS=", f); - serialize_in_addrs(f, addresses, r); - fputs("\n", f); + serialize_in_addrs(f, addresses, r, false, NULL); + fputc('\n', f); } r = sd_dhcp_lease_get_ntp(lease, &addresses); if (r > 0) { fputs("NTP=", f); - serialize_in_addrs(f, addresses, r); - fputs("\n", f); + serialize_in_addrs(f, addresses, r, false, NULL); + fputc('\n', f); } r = sd_dhcp_lease_get_domainname(lease, &string); @@ -917,7 +903,7 @@ int dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r > 0) { fputs("DOMAIN_SEARCH_LIST=", f); fputstrv(f, search_domains, NULL, NULL); - fputs("\n", f); + fputc('\n', f); } r = sd_dhcp_lease_get_hostname(lease, &string); @@ -1080,9 +1066,11 @@ int dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { } if (router) { - r = inet_pton(AF_INET, router, &lease->router); - if (r <= 0) - log_debug("Failed to parse router %s, ignoring.", router); + r = deserialize_in_addrs(&lease->router, router); + if (r < 0) + log_debug_errno(r, "Failed to deserialize router addresses %s, ignoring: %m", router); + else + lease->router_size = r; } if (netmask) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 593ffd1b6..68b41dfb6 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -1112,6 +1112,12 @@ static int client_receive_message( assert(client->event); buflen = next_datagram_size_fd(fd); + if (buflen == -ENETDOWN) { + /* the link is down. Don't return an error or the I/O event + source will be disconnected and we won't be able to receive + packets again when the link comes back. */ + return 0; + } if (buflen < 0) return buflen; @@ -1121,7 +1127,8 @@ static int client_receive_message( len = recv(fd, message, buflen, 0); if (len < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + /* see comment above for why we shouldn't error out on ENETDOWN. */ + if (IN_SET(errno, EAGAIN, EINTR, ENETDOWN)) return 0; return log_dhcp6_client_errno(client, errno, "Could not receive message from UDP socket: %m"); diff --git a/src/libsystemd-network/sd-ipv4acd.c b/src/libsystemd-network/sd-ipv4acd.c index 59359aec7..c8e34497f 100644 --- a/src/libsystemd-network/sd-ipv4acd.c +++ b/src/libsystemd-network/sd-ipv4acd.c @@ -21,7 +21,7 @@ #include "random-util.h" #include "siphash24.h" #include "string-util.h" -#include "util.h" +#include "time-util.h" /* Constants from the RFC */ #define PROBE_WAIT_USEC (1U * USEC_PER_SEC) diff --git a/src/libsystemd-network/sd-ipv4ll.c b/src/libsystemd-network/sd-ipv4ll.c index e451dff74..a59a95232 100644 --- a/src/libsystemd-network/sd-ipv4ll.c +++ b/src/libsystemd-network/sd-ipv4ll.c @@ -218,28 +218,21 @@ static int ipv4ll_pick_address(sd_ipv4ll *ll) { return sd_ipv4ll_set_address(ll, &(struct in_addr) { addr }); } -int sd_ipv4ll_restart(sd_ipv4ll *ll) { - ll->address = 0; - - return sd_ipv4ll_start(ll); -} - #define MAC_HASH_KEY SD_ID128_MAKE(df,04,22,98,3f,ad,14,52,f9,87,2e,d1,9c,70,e2,f2) -int sd_ipv4ll_start(sd_ipv4ll *ll) { +static int ipv4ll_start_internal(sd_ipv4ll *ll, bool reset_generation) { int r; bool picked_address = false; assert_return(ll, -EINVAL); assert_return(!ether_addr_is_null(&ll->mac), -EINVAL); - assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); /* If no random seed is set, generate some from the MAC address */ if (!ll->seed_set) ll->seed.value = htole64(siphash24(ll->mac.ether_addr_octet, ETH_ALEN, MAC_HASH_KEY.bytes)); - /* Restart the generation counter. */ - ll->seed.generation = 0; + if (reset_generation) + ll->seed.generation = 0; if (ll->address == 0) { r = ipv4ll_pick_address(ll); @@ -263,6 +256,19 @@ int sd_ipv4ll_start(sd_ipv4ll *ll) { return 0; } +int sd_ipv4ll_start(sd_ipv4ll *ll) { + assert_return(ll, -EINVAL); + assert_return(sd_ipv4ll_is_running(ll) == 0, -EBUSY); + + return ipv4ll_start_internal(ll, true); +} + +int sd_ipv4ll_restart(sd_ipv4ll *ll) { + ll->address = 0; + + return ipv4ll_start_internal(ll, false); +} + static void ipv4ll_client_notify(sd_ipv4ll *ll, int event) { assert(ll); @@ -298,11 +304,7 @@ void ipv4ll_on_acd(sd_ipv4acd *acd, int event, void *userdata) { ll->claimed_address = 0; } else { - r = ipv4ll_pick_address(ll); - if (r < 0) - goto error; - - r = sd_ipv4acd_start(ll->acd); + r = sd_ipv4ll_restart(ll); if (r < 0) goto error; } diff --git a/src/libsystemd-network/sd-lldp.c b/src/libsystemd-network/sd-lldp.c index 969fc7105..1f28c5731 100644 --- a/src/libsystemd-network/sd-lldp.c +++ b/src/libsystemd-network/sd-lldp.c @@ -13,7 +13,9 @@ #include "lldp-internal.h" #include "lldp-neighbor.h" #include "lldp-network.h" +#include "memory-util.h" #include "socket-util.h" +#include "sort-util.h" #include "string-table.h" #define LLDP_DEFAULT_NEIGHBORS_MAX 128U diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 32c20b1e4..5e711713d 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -13,13 +13,13 @@ #include "fd-util.h" #include "icmp6-util.h" #include "in-addr-util.h" +#include "memory-util.h" #include "ndisc-internal.h" #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) diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 098e01fb8..08433adb2 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -18,12 +18,12 @@ #include "in-addr-util.h" #include "io-util.h" #include "macro.h" +#include "memory-util.h" #include "radv-internal.h" #include "random-util.h" #include "socket-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" _public_ int sd_radv_new(sd_radv **ret) { _cleanup_(sd_radv_unrefp) sd_radv *ra = NULL; diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index fe6788d91..0431e2c3f 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -423,6 +423,7 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, sd_event *e = userdata; sd_dhcp_lease *lease; struct in_addr addr; + const struct in_addr *addrs; assert_se(client); assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE); @@ -438,9 +439,9 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[285], sizeof(addr.s_addr)) == 0); - assert_se(sd_dhcp_lease_get_router(lease, &addr) >= 0); - assert_se(memcmp(&addr.s_addr, &test_addr_acq_ack[308], - sizeof(addr.s_addr)) == 0); + assert_se(sd_dhcp_lease_get_router(lease, &addrs) == 1); + assert_se(memcmp(&addrs[0].s_addr, &test_addr_acq_ack[308], + sizeof(addrs[0].s_addr)) == 0); if (verbose) printf(" DHCP address acquired\n"); diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index d84859c05..56bd690cb 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -7,7 +7,7 @@ #include "dhcp-internal.h" #include "dhcp-protocol.h" #include "macro.h" -#include "util.h" +#include "memory-util.h" struct option_desc { uint8_t sname[64]; diff --git a/src/libsystemd-network/test-dhcp6-client.c b/src/libsystemd-network/test-dhcp6-client.c index fa94b3cb7..00056aa9e 100644 --- a/src/libsystemd-network/test-dhcp6-client.c +++ b/src/libsystemd-network/test-dhcp6-client.c @@ -18,9 +18,10 @@ #include "dhcp6-protocol.h" #include "fd-util.h" #include "macro.h" +#include "memory-util.h" #include "socket-util.h" #include "tests.h" -#include "util.h" +#include "time-util.h" #include "virt.h" static struct ether_addr mac_addr = { diff --git a/src/libsystemd-network/test-lldp.c b/src/libsystemd-network/test-lldp.c index b6c896f0a..7406f94ce 100644 --- a/src/libsystemd-network/test-lldp.c +++ b/src/libsystemd-network/test-lldp.c @@ -15,6 +15,7 @@ #include "lldp-network.h" #include "macro.h" #include "string-util.h" +#include "tests.h" #define TEST_LLDP_PORT "em1" #define TEST_LLDP_TYPE_SYSTEM_NAME "systemd-lldp" @@ -365,7 +366,7 @@ static void test_multiple_neighbors_sorted(sd_event *e) { int main(int argc, char *argv[]) { _cleanup_(sd_event_unrefp) sd_event *e = NULL; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); /* LLDP reception tests */ assert_se(sd_event_new(&e) == 0); diff --git a/src/libsystemd-network/test-ndisc-ra.c b/src/libsystemd-network/test-ndisc-ra.c index c4c1c8114..7dc44e567 100644 --- a/src/libsystemd-network/test-ndisc-ra.c +++ b/src/libsystemd-network/test-ndisc-ra.c @@ -5,6 +5,7 @@ #include #include +#include #include "sd-radv.h" diff --git a/src/libsystemd-network/test-ndisc-rs.c b/src/libsystemd-network/test-ndisc-rs.c index caf94d10f..c8ee1ec31 100644 --- a/src/libsystemd-network/test-ndisc-rs.c +++ b/src/libsystemd-network/test-ndisc-rs.c @@ -5,6 +5,7 @@ #include #include +#include #include "sd-ndisc.h" diff --git a/src/libsystemd/sd-bus/bus-container.c b/src/libsystemd/sd-bus/bus-container.c index 2cfeefc2c..40b0e8a94 100644 --- a/src/libsystemd/sd-bus/bus-container.c +++ b/src/libsystemd/sd-bus/bus-container.c @@ -7,6 +7,7 @@ #include "bus-internal.h" #include "bus-socket.h" #include "fd-util.h" +#include "namespace-util.h" #include "process-util.h" #include "util.h" diff --git a/src/libsystemd/sd-bus/bus-control.c b/src/libsystemd/sd-bus/bus-control.c index 7775d2b37..5f2502076 100644 --- a/src/libsystemd/sd-bus/bus-control.c +++ b/src/libsystemd/sd-bus/bus-control.c @@ -825,6 +825,7 @@ int bus_add_match_internal( "s", e); } + int bus_add_match_internal_async( sd_bus *bus, sd_bus_slot **ret_slot, diff --git a/src/libsystemd/sd-bus/bus-convenience.c b/src/libsystemd/sd-bus/bus-convenience.c index c4d4016fc..4ec061644 100644 --- a/src/libsystemd/sd-bus/bus-convenience.c +++ b/src/libsystemd/sd-bus/bus-convenience.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "bus-internal.h" #include "bus-message.h" #include "bus-signature.h" diff --git a/src/libsystemd/sd-bus/bus-error.c b/src/libsystemd/sd-bus/bus-error.c index dc952375b..96319645d 100644 --- a/src/libsystemd/sd-bus/bus-error.c +++ b/src/libsystemd/sd-bus/bus-error.c @@ -12,6 +12,7 @@ #include "alloc-util.h" #include "bus-error.h" #include "errno-list.h" +#include "errno-util.h" #include "string-util.h" #include "util.h" diff --git a/src/libsystemd/sd-bus/bus-internal.c b/src/libsystemd/sd-bus/bus-internal.c index 40acae213..dff39cb13 100644 --- a/src/libsystemd/sd-bus/bus-internal.c +++ b/src/libsystemd/sd-bus/bus-internal.c @@ -43,7 +43,7 @@ bool object_path_is_valid(const char *p) { if (slash) return false; - return true; + return (q - p) <= BUS_PATH_SIZE_MAX; } char* object_path_startswith(const char *a, const char *b) { @@ -97,7 +97,7 @@ bool interface_name_is_valid(const char *p) { dot = false; } - if (q - p > 255) + if (q - p > SD_BUS_MAXIMUM_NAME_LENGTH) return false; if (dot) @@ -139,7 +139,7 @@ bool service_name_is_valid(const char *p) { dot = false; } - if (q - p > 255) + if (q - p > SD_BUS_MAXIMUM_NAME_LENGTH) return false; if (dot) @@ -170,7 +170,7 @@ bool member_name_is_valid(const char *p) { return false; } - if (q - p > 255) + if (q - p > SD_BUS_MAXIMUM_NAME_LENGTH) return false; return true; diff --git a/src/libsystemd/sd-bus/bus-internal.h b/src/libsystemd/sd-bus/bus-internal.h index f208b294d..d7fcb9329 100644 --- a/src/libsystemd/sd-bus/bus-internal.h +++ b/src/libsystemd/sd-bus/bus-internal.h @@ -13,9 +13,13 @@ #include "hashmap.h" #include "list.h" #include "prioq.h" -#include "refcnt.h" #include "socket-util.h" -#include "util.h" +#include "time-util.h" + +/* Note that we use the new /run prefix here (instead of /var/run) since we require them to be aliases and + * that way we become independent of /var being mounted */ +#define DEFAULT_SYSTEM_BUS_ADDRESS "unix:path=/run/dbus/system_bus_socket" +#define DEFAULT_USER_BUS_ADDRESS_FMT "unix:path=%s/bus" struct reply_callback { sd_bus_message_handler_t callback; @@ -60,11 +64,11 @@ struct node { struct node_callback { struct node *node; - bool is_fallback; - sd_bus_message_handler_t callback; - + bool is_fallback:1; unsigned last_iteration; + sd_bus_message_handler_t callback; + LIST_FIELDS(struct node_callback, callbacks); }; @@ -87,13 +91,13 @@ struct node_object_manager { struct node_vtable { struct node *node; + bool is_fallback:1; + unsigned last_iteration; + char *interface; - bool is_fallback; const sd_bus_vtable *vtable; sd_bus_object_find_t find; - unsigned last_iteration; - LIST_FIELDS(struct node_vtable, vtables); }; @@ -119,9 +123,6 @@ typedef enum BusSlotType { struct sd_bus_slot { unsigned n_ref; - sd_bus *bus; - void *userdata; - sd_bus_destroy_t destroy_callback; BusSlotType type:5; /* Slots can be "floating" or not. If they are not floating (the usual case) then they reference the bus object @@ -133,6 +134,11 @@ struct sd_bus_slot { bool floating:1; bool match_added:1; + + sd_bus *bus; + void *userdata; + sd_bus_destroy_t destroy_callback; + char *description; LIST_FIELDS(sd_bus_slot, slots); @@ -171,15 +177,7 @@ enum bus_auth { }; struct sd_bus { - /* We use atomic ref counting here since sd_bus_message - objects retain references to their originating sd_bus but - we want to allow them to be processed in a different - thread. We won't provide full thread safety, but only the - bare minimum that makes it possible to use sd_bus and - sd_bus_message objects independently and on different - threads as long as each object is used only once at the - same time. */ - RefCount n_ref; + unsigned n_ref; enum bus_state state; int input_fd, output_fd; @@ -213,17 +211,17 @@ struct sd_bus { bool connected_signal:1; bool close_on_exit:1; - int use_memfd; + signed int use_memfd:2; void *rbuffer; size_t rbuffer_size; sd_bus_message **rqueue; - unsigned rqueue_size; + size_t rqueue_size; size_t rqueue_allocated; sd_bus_message **wqueue; - unsigned wqueue_size; + size_t wqueue_size; size_t windex; size_t wqueue_allocated; @@ -244,8 +242,8 @@ struct sd_bus { union sockaddr_union sockaddr; socklen_t sockaddr_size; - char *machine; pid_t nspid; + char *machine; sd_id128_t server_id; @@ -255,9 +253,9 @@ struct sd_bus { int last_connect_error; enum bus_auth auth; - size_t auth_rbegin; - struct iovec auth_iovec[3]; unsigned auth_index; + struct iovec auth_iovec[3]; + size_t auth_rbegin; char *auth_buffer; usec_t auth_timeout; @@ -274,8 +272,6 @@ struct sd_bus { char *exec_path; char **exec_argv; - unsigned iteration_counter; - /* We do locking around the memfd cache, since we want to * allow people to process a sd_bus_message in a different * thread then it was generated on and free it there. Since @@ -289,6 +285,8 @@ struct sd_bus { pid_t original_pid; pid_t busexec_pid; + unsigned iteration_counter; + sd_event_source *input_io_event_source; sd_event_source *output_io_event_source; sd_event_source *time_event_source; @@ -297,13 +295,14 @@ struct sd_bus { sd_event *event; int event_priority; + pid_t tid; + sd_bus_message *current_message; sd_bus_slot *current_slot; sd_bus_message_handler_t current_handler; void *current_userdata; sd_bus **default_bus_ptr; - pid_t tid; char *description; char *patch_sender; @@ -332,6 +331,10 @@ struct sd_bus { #define BUS_MESSAGE_SIZE_MAX (128*1024*1024) #define BUS_AUTH_SIZE_MAX (64*1024) +/* Note that the D-Bus specification states that bus paths shall have no size limit. We enforce here one + * anyway, since truly unbounded strings are a security problem. The limit we pick is relatively large however, + * to not clash unnecessarily with real-life applications. */ +#define BUS_PATH_SIZE_MAX (64*1024) #define BUS_CONTAINER_DEPTH 128 diff --git a/src/libsystemd/sd-bus/bus-introspect.c b/src/libsystemd/sd-bus/bus-introspect.c index f623dd9ce..29a5ef715 100644 --- a/src/libsystemd/sd-bus/bus-introspect.c +++ b/src/libsystemd/sd-bus/bus-introspect.c @@ -4,12 +4,13 @@ #include "bus-internal.h" #include "bus-introspect.h" +#include "bus-objects.h" #include "bus-protocol.h" #include "bus-signature.h" #include "fd-util.h" #include "fileio.h" +#include "memory-util.h" #include "string-util.h" -#include "util.h" int introspect_begin(struct introspect *i, bool trusted) { assert(i); @@ -86,7 +87,9 @@ static void introspect_write_flags(struct introspect *i, int type, uint64_t flag fputs(" \n", i->f); } -static int introspect_write_arguments(struct introspect *i, const char *signature, const char *direction) { +/* Note that "names" is both an input and an output parameter. It initially points to the first argument name in a + NULL-separated list of strings, and is then advanced with each argument, and the resulting pointer is returned. */ +static int introspect_write_arguments(struct introspect *i, const char *signature, const char **names, const char *direction) { int r; for (;;) { @@ -101,6 +104,11 @@ static int introspect_write_arguments(struct introspect *i, const char *signatur fprintf(i->f, " f, " name=\"%s\"", *names); + *names += strlen(*names) + 1; + } + if (direction) fprintf(i->f, " direction=\"%s\"/>\n", direction); else @@ -111,10 +119,13 @@ static int introspect_write_arguments(struct introspect *i, const char *signatur } int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { + const sd_bus_vtable *vtable = v; + const char *names = ""; + assert(i); assert(v); - for (; v->type != _SD_BUS_VTABLE_END; v++) { + for (; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) { /* Ignore methods, signals and properties that are * marked "hidden", but do show the interface @@ -132,8 +143,10 @@ int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { case _SD_BUS_VTABLE_METHOD: fprintf(i->f, " \n", v->x.method.member); - introspect_write_arguments(i, strempty(v->x.method.signature), "in"); - introspect_write_arguments(i, strempty(v->x.method.result), "out"); + if (bus_vtable_has_names(vtable)) + names = strempty(v->x.method.names); + introspect_write_arguments(i, strempty(v->x.method.signature), &names, "in"); + introspect_write_arguments(i, strempty(v->x.method.result), &names, "out"); introspect_write_flags(i, v->type, v->flags); fputs(" \n", i->f); break; @@ -150,7 +163,9 @@ int introspect_write_interface(struct introspect *i, const sd_bus_vtable *v) { case _SD_BUS_VTABLE_SIGNAL: fprintf(i->f, " \n", v->x.signal.member); - introspect_write_arguments(i, strempty(v->x.signal.signature), NULL); + if (bus_vtable_has_names(vtable)) + names = strempty(v->x.method.names); + introspect_write_arguments(i, strempty(v->x.signal.signature), &names, NULL); introspect_write_flags(i, v->type, v->flags); fputs(" \n", i->f); break; diff --git a/src/libsystemd/sd-bus/bus-kernel.c b/src/libsystemd/sd-bus/bus-kernel.c index 34c8a9f8c..b74cfa6ab 100644 --- a/src/libsystemd/sd-bus/bus-kernel.c +++ b/src/libsystemd/sd-bus/bus-kernel.c @@ -31,7 +31,7 @@ #include "string-util.h" #include "strv.h" #include "user-util.h" -#include "util.h" +#include "memory-util.h" void close_and_munmap(int fd, void *address, size_t size) { if (size > 0) diff --git a/src/libsystemd/sd-bus/bus-match.c b/src/libsystemd/sd-bus/bus-match.c index 9642de10c..266dd7f1d 100644 --- a/src/libsystemd/sd-bus/bus-match.c +++ b/src/libsystemd/sd-bus/bus-match.c @@ -10,6 +10,7 @@ #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" +#include "sort-util.h" #include "string-util.h" #include "strv.h" diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c index bb7e09c94..427d42f29 100644 --- a/src/libsystemd/sd-bus/bus-message.c +++ b/src/libsystemd/sd-bus/bus-message.c @@ -16,11 +16,11 @@ #include "fd-util.h" #include "io-util.h" #include "memfd-util.h" +#include "memory-util.h" #include "string-util.h" #include "strv.h" #include "time-util.h" #include "utf8.h" -#include "util.h" static int message_append_basic(sd_bus_message *m, char type, const void *p, const void **stored); @@ -118,7 +118,8 @@ static sd_bus_message* message_free(sd_bus_message *m) { message_reset_parts(m); - sd_bus_unref(m->bus); + /* Note that we don't unref m->bus here. That's already done by sd_bus_message_unref() as each user + * reference to the bus message also is considered a reference to the bus connection itself. */ if (m->free_fds) { close_many(m->fds, m->n_fds); @@ -136,8 +137,6 @@ static sd_bus_message* message_free(sd_bus_message *m) { return mfree(m); } -DEFINE_TRIVIAL_CLEANUP_FUNC(sd_bus_message*, message_free); - static void *message_extend_fields(sd_bus_message *m, size_t align, size_t sz, bool add_offset) { void *op, *np; size_t old_size, new_size, start; @@ -285,7 +284,7 @@ static int message_append_field_signature( /* dbus1 doesn't allow signatures over 8bit, let's enforce * this globally, to not risk convertability */ l = strlen(s); - if (l > 255) + if (l > SD_BUS_MAXIMUM_SIGNATURE_LENGTH) return -EINVAL; /* Signature "(yv)" where the variant contains "g" */ @@ -459,7 +458,6 @@ int bus_message_from_header( if (!m) return -ENOMEM; - m->n_ref = 1; m->sealed = true; m->header = header; m->header_accessible = header_accessible; @@ -513,7 +511,9 @@ int bus_message_from_header( m->creds.mask |= SD_BUS_CREDS_SELINUX_CONTEXT; } + m->n_ref = 1; m->bus = sd_bus_ref(bus); + *ret = TAKE_PTR(m); return 0; @@ -528,7 +528,7 @@ int bus_message_from_malloc( const char *label, sd_bus_message **ret) { - _cleanup_(message_freep) sd_bus_message *m = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL; size_t sz; int r; @@ -585,13 +585,13 @@ _public_ int sd_bus_message_new( return -ENOMEM; t->n_ref = 1; + t->bus = sd_bus_ref(bus); t->header = (struct bus_header*) ((uint8_t*) t + ALIGN(sizeof(struct sd_bus_message))); t->header->endian = BUS_NATIVE_ENDIAN; t->header->type = type; t->header->version = bus->message_version; t->allow_fds = bus->can_fds || !IN_SET(bus->state, BUS_HELLO, BUS_RUNNING); t->root_container.need_offsets = BUS_MESSAGE_IS_GVARIANT(t); - t->bus = sd_bus_ref(bus); if (bus->allow_interactive_authorization) t->header->flags |= BUS_MESSAGE_ALLOW_INTERACTIVE_AUTHORIZATION; @@ -647,7 +647,7 @@ _public_ int sd_bus_message_new_method_call( const char *interface, const char *member) { - _cleanup_(message_freep) sd_bus_message *t = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *t = NULL; int r; assert_return(bus, -ENOTCONN); @@ -692,7 +692,7 @@ static int message_new_reply( uint8_t type, sd_bus_message **m) { - _cleanup_(message_freep) sd_bus_message *t = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *t = NULL; uint64_t cookie; int r; @@ -743,7 +743,7 @@ _public_ int sd_bus_message_new_method_error( sd_bus_message **m, const sd_bus_error *e) { - _cleanup_(message_freep) sd_bus_message *t = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *t = NULL; int r; assert_return(sd_bus_error_is_set(e), -EINVAL); @@ -846,7 +846,7 @@ int bus_message_new_synthetic_error( const sd_bus_error *e, sd_bus_message **m) { - _cleanup_(message_freep) sd_bus_message *t = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *t = NULL; int r; assert(bus); @@ -890,7 +890,80 @@ int bus_message_new_synthetic_error( return 0; } -DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus_message, sd_bus_message, message_free); + +_public_ sd_bus_message* sd_bus_message_ref(sd_bus_message *m) { + if (!m) + return NULL; + + /* We are fine if this message so far was either explicitly reffed or not reffed but queued into at + * least one bus connection object. */ + assert(m->n_ref > 0 || m->n_queued > 0); + + m->n_ref++; + + /* Each user reference to a bus message shall also be considered a ref on the bus */ + sd_bus_ref(m->bus); + return m; +} + +_public_ sd_bus_message* sd_bus_message_unref(sd_bus_message *m) { + if (!m) + return NULL; + + assert(m->n_ref > 0); + + sd_bus_unref(m->bus); /* Each regular ref is also a ref on the bus connection. Let's hence drop it + * here. Note we have to do this before decrementing our own n_ref here, since + * otherwise, if this message is currently queued sd_bus_unref() might call + * bus_message_unref_queued() for this which might then destroy the message + * while we are still processing it. */ + m->n_ref--; + + if (m->n_ref > 0 || m->n_queued > 0) + return NULL; + + /* Unset the bus field if neither the user has a reference nor this message is queued. We are careful + * to reset the field only after the last reference to the bus is dropped, after all we might keep + * multiple references to the bus, once for each reference kept on outselves. */ + m->bus = NULL; + + return message_free(m); +} + +sd_bus_message* bus_message_ref_queued(sd_bus_message *m, sd_bus *bus) { + if (!m) + return NULL; + + /* If this is a different bus than the message is associated with, then implicitly turn this into a + * regular reference. This means that you can create a memory leak by enqueuing a message generated + * on one bus onto another at the same time as enqueueing a message from the second one on the first, + * as we'll not detect the cyclic references there. */ + if (bus != m->bus) + return sd_bus_message_ref(m); + + assert(m->n_ref > 0 || m->n_queued > 0); + m->n_queued++; + + return m; +} + +sd_bus_message* bus_message_unref_queued(sd_bus_message *m, sd_bus *bus) { + if (!m) + return NULL; + + if (bus != m->bus) + return sd_bus_message_unref(m); + + assert(m->n_queued > 0); + m->n_queued--; + + if (m->n_ref > 0 || m->n_queued > 0) + return NULL; + + m->bus = NULL; + + return message_free(m); +} _public_ int sd_bus_message_get_type(sd_bus_message *m, uint8_t *type) { assert_return(m, -EINVAL); @@ -5079,7 +5152,7 @@ int bus_message_parse_fields(sd_bus_message *m) { return -EBADMSG; if (*p == 0) { - char *k; + _cleanup_free_ char *k = NULL; size_t l; /* We found the beginning of the signature @@ -5097,6 +5170,9 @@ int bus_message_parse_fields(sd_bus_message *m) { if (!k) return -ENOMEM; + if (!signature_is_valid(k, true)) + return -EBADMSG; + free_and_replace(m->root_container.signature, k); break; } diff --git a/src/libsystemd/sd-bus/bus-message.h b/src/libsystemd/sd-bus/bus-message.h index 0115437d2..a7c4f81c4 100644 --- a/src/libsystemd/sd-bus/bus-message.h +++ b/src/libsystemd/sd-bus/bus-message.h @@ -48,7 +48,16 @@ struct bus_body_part { }; struct sd_bus_message { - unsigned n_ref; + /* Caveat: a message can be referenced in two different ways: the main (user-facing) way will also + * pin the bus connection object the message is associated with. The secondary way ("queued") is used + * when a message is in the read or write queues of the bus connection object, which will not pin the + * bus connection object. This is necessary so that we don't have to have a pair of cyclic references + * between a message that is queued and its connection: as soon as a message is only referenced by + * the connection (by means of being queued) and the connection itself has no other references it + * will be freed. */ + + unsigned n_ref; /* Counter of references that pin the connection */ + unsigned n_queued; /* Counter of references that do not pin the connection */ sd_bus *bus; @@ -211,3 +220,6 @@ int bus_message_remarshal(sd_bus *bus, sd_bus_message **m); 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); + +sd_bus_message* bus_message_ref_queued(sd_bus_message *m, sd_bus *bus); +sd_bus_message* bus_message_unref_queued(sd_bus_message *m, sd_bus *bus); diff --git a/src/libsystemd/sd-bus/bus-objects.c b/src/libsystemd/sd-bus/bus-objects.c index 58329f3fe..d9fc25605 100644 --- a/src/libsystemd/sd-bus/bus-objects.c +++ b/src/libsystemd/sd-bus/bus-objects.c @@ -736,7 +736,8 @@ static int vtable_append_all_properties( if (c->vtable[0].flags & SD_BUS_VTABLE_HIDDEN) return 1; - for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + v = c->vtable; + for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) { if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) continue; @@ -1133,7 +1134,8 @@ static int object_manager_serialize_path_and_fallbacks( const char *path, sd_bus_error *error) { - char *prefix; + _cleanup_free_ char *prefix = NULL; + size_t pl; int r; assert(bus); @@ -1149,7 +1151,12 @@ static int object_manager_serialize_path_and_fallbacks( return 0; /* Second, add fallback vtables registered for any of the prefixes */ - prefix = newa(char, strlen(path) + 1); + pl = strlen(path); + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { r = object_manager_serialize_path(bus, reply, prefix, path, true, error); if (r < 0) @@ -1345,6 +1352,7 @@ static int object_find_and_run( } int bus_process_object(sd_bus *bus, sd_bus_message *m) { + _cleanup_free_ char *prefix = NULL; int r; size_t pl; bool found_object = false; @@ -1369,9 +1377,12 @@ int bus_process_object(sd_bus *bus, sd_bus_message *m) { assert(m->member); pl = strlen(m->path); - do { - char prefix[pl+1]; + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; + do { bus->nodes_modified = false; r = object_find_and_run(bus, m, m->path, false, &found_object); @@ -1498,9 +1509,15 @@ static int bus_find_parent_object_manager(sd_bus *bus, struct node **out, const n = hashmap_get(bus->nodes, path); if (!n) { - char *prefix; + _cleanup_free_ char *prefix = NULL; + size_t pl; + + pl = strlen(path); + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; - prefix = newa(char, strlen(path) + 1); OBJECT_PATH_FOREACH_PREFIX(prefix, path) { n = hashmap_get(bus->nodes, prefix); if (n) @@ -1610,6 +1627,99 @@ static int vtable_member_compare_func(const struct vtable_member *x, const struc DEFINE_PRIVATE_HASH_OPS(vtable_member_hash_ops, struct vtable_member, vtable_member_hash_func, vtable_member_compare_func); +typedef enum { + NAMES_FIRST_PART = 1 << 0, /* first part of argument name list (input names). It is reset by names_are_valid() */ + NAMES_PRESENT = 1 << 1, /* at least one argument name is present, so the names will checked. + This flag is set and used internally by names_are_valid(), but needs to be stored across calls for 2-parts list */ + NAMES_SINGLE_PART = 1 << 2, /* argument name list consisting of a single part */ +} names_flags; + +static bool names_are_valid(const char *signature, const char **names, names_flags *flags) { + int r; + + if ((*flags & NAMES_FIRST_PART || *flags & NAMES_SINGLE_PART) && **names != '\0') + *flags |= NAMES_PRESENT; + + for (;*flags & NAMES_PRESENT;) { + size_t l; + + if (!*signature) + break; + + r = signature_element_length(signature, &l); + if (r < 0) + return false; + + if (**names != '\0') { + if (!member_name_is_valid(*names)) + return false; + *names += strlen(*names) + 1; + } else if (*flags & NAMES_PRESENT) + return false; + + signature += l; + } + /* let's check if there are more argument names specified than the signature allows */ + if (*flags & NAMES_PRESENT && **names != '\0' && !(*flags & NAMES_FIRST_PART)) + return false; + *flags &= ~NAMES_FIRST_PART; + return true; +} + +/* the current version of this struct is defined in sd-bus-vtable.h, but we need to list here the historical versions + to make sure the calling code is compatible with one of these */ +struct sd_bus_vtable_original { + uint8_t type:8; + uint64_t flags:56; + union { + struct { + size_t element_size; + } start; + struct { + const char *member; + const char *signature; + const char *result; + sd_bus_message_handler_t handler; + size_t offset; + } method; + struct { + const char *member; + const char *signature; + } signal; + struct { + const char *member; + const char *signature; + sd_bus_property_get_t get; + sd_bus_property_set_t set; + size_t offset; + } property; + } x; +}; +/* Structure size up to v241 */ +#define VTABLE_ELEMENT_SIZE_ORIGINAL sizeof(struct sd_bus_vtable_original) +/* Current structure size */ +#define VTABLE_ELEMENT_SIZE sizeof(struct sd_bus_vtable) + +static int vtable_features(const sd_bus_vtable *vtable) { + if (vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_ORIGINAL) + return 0; + return vtable[0].x.start.features; +} + +bool bus_vtable_has_names(const sd_bus_vtable *vtable) { + return vtable_features(vtable) & _SD_BUS_VTABLE_PARAM_NAMES; +} + +const sd_bus_vtable* bus_vtable_next(const sd_bus_vtable *vtable, const sd_bus_vtable *v) { + if (vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_ORIGINAL) { + const struct sd_bus_vtable_original *v2 = (const struct sd_bus_vtable_original *)v; + v2++; + v = (const sd_bus_vtable*)v2; + } else /* current version */ + v++; + return v; +} + static int add_object_vtable_internal( sd_bus *bus, sd_bus_slot **slot, @@ -1625,6 +1735,8 @@ static int add_object_vtable_internal( const sd_bus_vtable *v; struct node *n; int r; + const char *names = ""; + names_flags nf; assert_return(bus, -EINVAL); assert_return(bus = bus_resolve(bus), -ENOPKG); @@ -1632,7 +1744,9 @@ static int add_object_vtable_internal( assert_return(interface_name_is_valid(interface), -EINVAL); assert_return(vtable, -EINVAL); assert_return(vtable[0].type == _SD_BUS_VTABLE_START, -EINVAL); - assert_return(vtable[0].x.start.element_size == sizeof(struct sd_bus_vtable), -EINVAL); + assert_return(vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE_ORIGINAL || + vtable[0].x.start.element_size == VTABLE_ELEMENT_SIZE, + -EINVAL); assert_return(!bus_pid_changed(bus), -ECHILD); assert_return(!streq(interface, "org.freedesktop.DBus.Properties") && !streq(interface, "org.freedesktop.DBus.Introspectable") && @@ -1684,16 +1798,23 @@ static int add_object_vtable_internal( goto fail; } - for (v = s->node_vtable.vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + v = s->node_vtable.vtable; + for (v = bus_vtable_next(vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(vtable, v)) { switch (v->type) { case _SD_BUS_VTABLE_METHOD: { struct vtable_member *m; + nf = NAMES_FIRST_PART; + + if (bus_vtable_has_names(vtable)) + names = strempty(v->x.method.names); if (!member_name_is_valid(v->x.method.member) || !signature_is_valid(strempty(v->x.method.signature), false) || !signature_is_valid(strempty(v->x.method.result), false) || + !names_are_valid(strempty(v->x.method.signature), &names, &nf) || + !names_are_valid(strempty(v->x.method.result), &names, &nf) || !(v->x.method.handler || (isempty(v->x.method.signature) && isempty(v->x.method.result))) || v->flags & (SD_BUS_VTABLE_PROPERTY_CONST|SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE|SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION)) { r = -EINVAL; @@ -1770,9 +1891,14 @@ static int add_object_vtable_internal( } case _SD_BUS_VTABLE_SIGNAL: + nf = NAMES_SINGLE_PART; + + if (bus_vtable_has_names(vtable)) + names = strempty(v->x.signal.names); if (!member_name_is_valid(v->x.signal.member) || !signature_is_valid(strempty(v->x.signal.signature), false) || + !names_are_valid(strempty(v->x.signal.signature), &names, &nf) || v->flags & SD_BUS_VTABLE_UNPRIVILEGED) { r = -EINVAL; goto fail; @@ -1977,7 +2103,8 @@ static int emit_properties_changed_on_interface( * we include all properties that are marked * as changing in the message. */ - for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + v = c->vtable; + for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) { if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) continue; @@ -2048,7 +2175,8 @@ static int emit_properties_changed_on_interface( } else { const sd_bus_vtable *v; - for (v = c->vtable+1; v->type != _SD_BUS_VTABLE_END; v++) { + v = c->vtable; + for (v = bus_vtable_next(c->vtable, v); v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(c->vtable, v)) { if (!IN_SET(v->type, _SD_BUS_VTABLE_PROPERTY, _SD_BUS_VTABLE_WRITABLE_PROPERTY)) continue; @@ -2083,8 +2211,9 @@ _public_ int sd_bus_emit_properties_changed_strv( const char *interface, char **names) { + _cleanup_free_ char *prefix = NULL; bool found_interface = false; - char *prefix; + size_t pl; int r; assert_return(bus, -EINVAL); @@ -2105,6 +2234,12 @@ _public_ int sd_bus_emit_properties_changed_strv( BUS_DONT_DESTROY(bus); + pl = strlen(path); + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; + do { bus->nodes_modified = false; @@ -2114,7 +2249,6 @@ _public_ int sd_bus_emit_properties_changed_strv( if (bus->nodes_modified) continue; - prefix = newa(char, strlen(path) + 1); OBJECT_PATH_FOREACH_PREFIX(prefix, path) { r = emit_properties_changed_on_interface(bus, prefix, path, interface, true, &found_interface, names); if (r != 0) @@ -2246,7 +2380,8 @@ static int object_added_append_all_prefix( static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { _cleanup_set_free_ Set *s = NULL; - char *prefix; + _cleanup_free_ char *prefix = NULL; + size_t pl; int r; assert(bus); @@ -2291,7 +2426,12 @@ static int object_added_append_all(sd_bus *bus, sd_bus_message *m, const char *p if (bus->nodes_modified) return 0; - prefix = newa(char, strlen(path) + 1); + pl = strlen(path); + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { r = object_added_append_all_prefix(bus, m, s, prefix, path, true); if (r < 0) @@ -2430,7 +2570,8 @@ static int object_removed_append_all_prefix( static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char *path) { _cleanup_set_free_ Set *s = NULL; - char *prefix; + _cleanup_free_ char *prefix = NULL; + size_t pl; int r; assert(bus); @@ -2462,7 +2603,12 @@ static int object_removed_append_all(sd_bus *bus, sd_bus_message *m, const char if (bus->nodes_modified) return 0; - prefix = newa(char, strlen(path) + 1); + pl = strlen(path); + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { r = object_removed_append_all_prefix(bus, m, s, prefix, path, true); if (r < 0) @@ -2612,7 +2758,8 @@ static int interfaces_added_append_one( const char *path, const char *interface) { - char *prefix; + _cleanup_free_ char *prefix = NULL; + size_t pl; int r; assert(bus); @@ -2626,7 +2773,12 @@ static int interfaces_added_append_one( if (bus->nodes_modified) return 0; - prefix = newa(char, strlen(path) + 1); + pl = strlen(path); + assert(pl <= BUS_PATH_SIZE_MAX); + prefix = new(char, pl + 1); + if (!prefix) + return -ENOMEM; + OBJECT_PATH_FOREACH_PREFIX(prefix, path) { r = interfaces_added_append_one_prefix(bus, m, prefix, path, interface, true); if (r != 0) diff --git a/src/libsystemd/sd-bus/bus-objects.h b/src/libsystemd/sd-bus/bus-objects.h index a119ff95c..b45fe6323 100644 --- a/src/libsystemd/sd-bus/bus-objects.h +++ b/src/libsystemd/sd-bus/bus-objects.h @@ -3,5 +3,7 @@ #include "bus-internal.h" +const sd_bus_vtable* bus_vtable_next(const sd_bus_vtable *vtable, const sd_bus_vtable *v); +bool bus_vtable_has_names(const sd_bus_vtable *vtable); int bus_process_object(sd_bus *bus, sd_bus_message *m); void bus_node_gc(sd_bus *b, struct node *n); diff --git a/src/libsystemd/sd-bus/bus-signature.c b/src/libsystemd/sd-bus/bus-signature.c index 1ecd6e8b7..b420ba368 100644 --- a/src/libsystemd/sd-bus/bus-signature.c +++ b/src/libsystemd/sd-bus/bus-signature.c @@ -144,5 +144,5 @@ bool signature_is_valid(const char *s, bool allow_dict_entry) { p += t; } - return p - s <= 255; + return p - s <= SD_BUS_MAXIMUM_SIGNATURE_LENGTH; } diff --git a/src/libsystemd/sd-bus/bus-slot.c b/src/libsystemd/sd-bus/bus-slot.c index c9aca07f9..f90a7f05c 100644 --- a/src/libsystemd/sd-bus/bus-slot.c +++ b/src/libsystemd/sd-bus/bus-slot.c @@ -117,7 +117,7 @@ void bus_slot_disconnect(sd_bus_slot *slot, bool unref) { if (slot->node_vtable.node && slot->node_vtable.interface && slot->node_vtable.vtable) { const sd_bus_vtable *v; - for (v = slot->node_vtable.vtable; v->type != _SD_BUS_VTABLE_END; v++) { + for (v = slot->node_vtable.vtable; v->type != _SD_BUS_VTABLE_END; v = bus_vtable_next(slot->node_vtable.vtable, v)) { struct vtable_member *x = NULL; switch (v->type) { diff --git a/src/libsystemd/sd-bus/bus-socket.c b/src/libsystemd/sd-bus/bus-socket.c index 441b4a816..9221b33d4 100644 --- a/src/libsystemd/sd-bus/bus-socket.c +++ b/src/libsystemd/sd-bus/bus-socket.c @@ -18,6 +18,7 @@ #include "hexdecoct.h" #include "io-util.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "path-util.h" #include "process-util.h" @@ -28,7 +29,6 @@ #include "string-util.h" #include "user-util.h" #include "utf8.h" -#include "util.h" #define SNDBUF_SIZE (8*1024*1024) @@ -158,17 +158,24 @@ static int bus_socket_write_auth(sd_bus *b) { } static int bus_socket_auth_verify_client(sd_bus *b) { - char *e, *f, *start; + char *d, *e, *f, *start; sd_id128_t peer; - unsigned i; int r; assert(b); - /* We expect two response lines: "OK" and possibly - * "AGREE_UNIX_FD" */ + /* + * We expect three response lines: + * "DATA\r\n" + * "OK \r\n" + * "AGREE_UNIX_FD\r\n" (optional) + */ - e = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2); + d = memmem_safe(b->rbuffer, b->rbuffer_size, "\r\n", 2); + if (!d) + return 0; + + e = memmem(d + 2, b->rbuffer_size - (d - (char*) b->rbuffer) - 2, "\r\n", 2); if (!e) return 0; @@ -183,22 +190,38 @@ static int bus_socket_auth_verify_client(sd_bus *b) { start = e + 2; } - /* Nice! We got all the lines we need. First check the OK - * line */ + /* Nice! We got all the lines we need. First check the DATA line. */ - if (e - (char*) b->rbuffer != 3 + 32) + if (d - (char*) b->rbuffer == 4) { + if (memcmp(b->rbuffer, "DATA", 4)) + return -EPERM; + } else if (d - (char*) b->rbuffer == 3 + 32) { + /* + * Old versions of the server-side implementation of `sd-bus` replied with "OK " to + * "AUTH" requests from a client, even if the "AUTH" line did not contain inlined + * arguments. Therefore, we also accept "OK " here, even though it is technically the + * wrong reply. We ignore the "" parameter, though, since it has no real value. + */ + if (memcmp(b->rbuffer, "OK ", 3)) + return -EPERM; + } else return -EPERM; - if (memcmp(b->rbuffer, "OK ", 3)) + /* Now check the OK line. */ + + if (e - d != 2 + 3 + 32) + return -EPERM; + + if (memcmp(d + 2, "OK ", 3)) return -EPERM; b->auth = b->anonymous_auth ? BUS_AUTH_ANONYMOUS : BUS_AUTH_EXTERNAL; - for (i = 0; i < 32; i += 2) { + for (unsigned i = 0; i < 32; i += 2) { int x, y; - x = unhexchar(((char*) b->rbuffer)[3 + i]); - y = unhexchar(((char*) b->rbuffer)[3 + i + 1]); + x = unhexchar(d[2 + 3 + i]); + y = unhexchar(d[2 + 3 + i + 1]); if (x < 0 || y < 0) return -EINVAL; @@ -212,7 +235,7 @@ static int bus_socket_auth_verify_client(sd_bus *b) { b->server_id = peer; - /* And possibly check the second line, too */ + /* And possibly check the third line, too */ if (f) b->can_fds = @@ -386,26 +409,36 @@ static int bus_socket_auth_verify_server(sd_bus *b) { if (line_begins(line, l, "AUTH ANONYMOUS")) { - r = verify_anonymous_token(b, line + 14, l - 14); + r = verify_anonymous_token(b, + line + strlen("AUTH ANONYMOUS"), + l - strlen("AUTH ANONYMOUS")); if (r < 0) return r; if (r == 0) r = bus_socket_auth_write(b, "REJECTED\r\n"); else { b->auth = BUS_AUTH_ANONYMOUS; - r = bus_socket_auth_write_ok(b); + if (l <= strlen("AUTH ANONYMOUS")) + r = bus_socket_auth_write(b, "DATA\r\n"); + else + r = bus_socket_auth_write_ok(b); } } else if (line_begins(line, l, "AUTH EXTERNAL")) { - r = verify_external_token(b, line + 13, l - 13); + r = verify_external_token(b, + line + strlen("AUTH EXTERNAL"), + l - strlen("AUTH EXTERNAL")); if (r < 0) return r; if (r == 0) r = bus_socket_auth_write(b, "REJECTED\r\n"); else { b->auth = BUS_AUTH_EXTERNAL; - r = bus_socket_auth_write_ok(b); + if (l <= strlen("AUTH EXTERNAL")) + r = bus_socket_auth_write(b, "DATA\r\n"); + else + r = bus_socket_auth_write_ok(b); } } else if (line_begins(line, l, "AUTH")) @@ -601,39 +634,39 @@ static void bus_get_peercred(sd_bus *b) { } static int bus_socket_start_auth_client(sd_bus *b) { - size_t l; - const char *auth_suffix, *auth_prefix; + static const char sasl_auth_anonymous[] = { + /* + * We use an arbitrary trace-string for the ANONYMOUS authentication. It can be used by the + * message broker to aid debugging of clients. We fully anonymize the connection and use a + * static default. + */ + "\0AUTH ANONYMOUS\r\n" + /* HEX a n o n y m o u s */ + "DATA 616e6f6e796d6f7573\r\n" + }; + static const char sasl_auth_external[] = { + "\0AUTH EXTERNAL\r\n" + "DATA\r\n" + }; + static const char sasl_negotiate_unix_fd[] = { + "NEGOTIATE_UNIX_FD\r\n" + }; + static const char sasl_begin[] = { + "BEGIN\r\n" + }; + size_t i = 0; assert(b); - if (b->anonymous_auth) { - auth_prefix = "\0AUTH ANONYMOUS "; - - /* For ANONYMOUS auth we send some arbitrary "trace" string */ - l = 9; - b->auth_buffer = hexmem("anonymous", l); - } else { - char text[DECIMAL_STR_MAX(uid_t) + 1]; - - auth_prefix = "\0AUTH EXTERNAL "; - - xsprintf(text, UID_FMT, geteuid()); - - l = strlen(text); - b->auth_buffer = hexmem(text, l); - } - - if (!b->auth_buffer) - return -ENOMEM; + if (b->anonymous_auth) + b->auth_iovec[i++] = IOVEC_MAKE((char*) sasl_auth_anonymous, sizeof(sasl_auth_anonymous) - 1); + else + b->auth_iovec[i++] = IOVEC_MAKE((char*) sasl_auth_external, sizeof(sasl_auth_external) - 1); if (b->accept_fd) - auth_suffix = "\r\nNEGOTIATE_UNIX_FD\r\nBEGIN\r\n"; - else - auth_suffix = "\r\nBEGIN\r\n"; + b->auth_iovec[i++] = IOVEC_MAKE_STRING(sasl_negotiate_unix_fd); - 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); + b->auth_iovec[i++] = IOVEC_MAKE_STRING(sasl_begin); return bus_socket_write_auth(b); } @@ -1097,21 +1130,25 @@ static int bus_socket_make_message(sd_bus *bus, size_t size) { bus->fds, bus->n_fds, NULL, &t); - if (r == -EBADMSG) + if (r == -EBADMSG) { log_debug_errno(r, "Received invalid message from connection %s, dropping.", strna(bus->description)); - else if (r < 0) { + free(bus->rbuffer); /* We want to drop current rbuffer and proceed with whatever remains in b */ + } else if (r < 0) { free(b); return r; } + /* rbuffer ownership was either transferred to t, or we got EBADMSG and dropped it. */ bus->rbuffer = b; bus->rbuffer_size -= size; bus->fds = NULL; bus->n_fds = 0; - if (t) - bus->rqueue[bus->rqueue_size++] = t; + if (t) { + bus->rqueue[bus->rqueue_size++] = bus_message_ref_queued(t, bus); + sd_bus_message_unref(t); + } return 1; } diff --git a/src/libsystemd/sd-bus/sd-bus.c b/src/libsystemd/sd-bus/sd-bus.c index 1ff858f32..3df7c86b8 100644 --- a/src/libsystemd/sd-bus/sd-bus.c +++ b/src/libsystemd/sd-bus/sd-bus.c @@ -28,16 +28,17 @@ #include "bus-util.h" #include "cgroup-util.h" #include "def.h" +#include "errno-util.h" #include "fd-util.h" #include "hexdecoct.h" #include "hostname-util.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "parse-util.h" #include "process-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" #define log_debug_bus_message(m) \ do { \ @@ -146,13 +147,13 @@ static void bus_reset_queues(sd_bus *b) { assert(b); while (b->rqueue_size > 0) - sd_bus_message_unref(b->rqueue[--b->rqueue_size]); + bus_message_unref_queued(b->rqueue[--b->rqueue_size], b); b->rqueue = mfree(b->rqueue); b->rqueue_allocated = 0; while (b->wqueue_size > 0) - sd_bus_message_unref(b->wqueue[--b->wqueue_size]); + bus_message_unref_queued(b->wqueue[--b->wqueue_size], b); b->wqueue = mfree(b->wqueue); b->wqueue_allocated = 0; @@ -236,7 +237,7 @@ _public_ int sd_bus_new(sd_bus **ret) { return -ENOMEM; *b = (sd_bus) { - .n_ref = REFCNT_INIT, + .n_ref = 1, .input_fd = -1, .output_fd = -1, .inotify_fd = -1, @@ -248,12 +249,12 @@ _public_ int sd_bus_new(sd_bus **ret) { .close_on_exit = true, }; - assert_se(pthread_mutex_init(&b->memfd_cache_mutex, NULL) == 0); - /* We guarantee that wqueue always has space for at least one entry */ if (!GREEDY_REALLOC(b->wqueue, b->wqueue_allocated, 1)) return -ENOMEM; + assert_se(pthread_mutex_init(&b->memfd_cache_mutex, NULL) == 0); + *ret = TAKE_PTR(b); return 0; } @@ -493,7 +494,7 @@ static int synthesize_connected_signal(sd_bus *bus) { /* Insert at the very front */ memmove(bus->rqueue + 1, bus->rqueue, sizeof(sd_bus_message*) * bus->rqueue_size); - bus->rqueue[0] = TAKE_PTR(m); + bus->rqueue[0] = bus_message_ref_queued(m, bus); bus->rqueue_size++; return 0; @@ -1585,7 +1586,7 @@ void bus_enter_closing(sd_bus *bus) { bus_set_state(bus, BUS_CLOSING); } -DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free); +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_bus, sd_bus, bus_free); _public_ int sd_bus_is_open(sd_bus *bus) { assert_return(bus, -EINVAL); @@ -1644,6 +1645,47 @@ _public_ int sd_bus_get_bus_id(sd_bus *bus, sd_id128_t *id) { return 0; } +#define COOKIE_CYCLED (UINT32_C(1) << 31) + +static uint64_t cookie_inc(uint64_t cookie) { + + /* Stay within the 32bit range, since classic D-Bus can't deal with more */ + if (cookie >= UINT32_MAX) + return COOKIE_CYCLED; /* Don't go back to zero, but use the highest bit for checking + * whether we are looping. */ + + return cookie + 1; +} + +static int next_cookie(sd_bus *b) { + uint64_t new_cookie; + + assert(b); + + new_cookie = cookie_inc(b->cookie); + + /* Small optimization: don't bother with checking for cookie reuse until we overran cookiespace at + * least once, but then do it thorougly. */ + if (FLAGS_SET(new_cookie, COOKIE_CYCLED)) { + uint32_t i; + + /* Check if the cookie is currently in use. If so, pick the next one */ + for (i = 0; i < COOKIE_CYCLED; i++) { + if (!ordered_hashmap_contains(b->reply_callbacks, &new_cookie)) + goto good; + + new_cookie = cookie_inc(new_cookie); + } + + /* Can't fulfill request */ + return -EBUSY; + } + +good: + b->cookie = new_cookie; + return 0; +} + static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { int r; @@ -1670,7 +1712,11 @@ static int bus_seal_message(sd_bus *b, sd_bus_message *m, usec_t timeout) { return r; } - return sd_bus_message_seal(m, ++b->cookie, timeout); + r = next_cookie(b); + if (r < 0) + return r; + + return sd_bus_message_seal(m, b->cookie, timeout); } static int bus_remarshal_message(sd_bus *b, sd_bus_message **m) { @@ -1766,7 +1812,7 @@ static int dispatch_wqueue(sd_bus *bus) { * anyway. */ bus->wqueue_size--; - sd_bus_message_unref(bus->wqueue[0]); + bus_message_unref_queued(bus->wqueue[0], bus); memmove(bus->wqueue, bus->wqueue + 1, sizeof(sd_bus_message*) * bus->wqueue_size); bus->windex = 0; @@ -1795,6 +1841,15 @@ int bus_rqueue_make_room(sd_bus *bus) { return 0; } +static void rqueue_drop_one(sd_bus *bus, size_t i) { + assert(bus); + assert(i < bus->rqueue_size); + + bus_message_unref_queued(bus->rqueue[i], bus); + memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); + bus->rqueue_size--; +} + static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd_bus_message **m) { int r, ret = 0; @@ -1809,10 +1864,8 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd for (;;) { if (bus->rqueue_size > 0) { /* Dispatch a queued message */ - - *m = bus->rqueue[0]; - bus->rqueue_size--; - memmove(bus->rqueue, bus->rqueue + 1, sizeof(sd_bus_message*) * bus->rqueue_size); + *m = sd_bus_message_ref(bus->rqueue[0]); + rqueue_drop_one(bus, 0); return 1; } @@ -1820,8 +1873,10 @@ static int dispatch_rqueue(sd_bus *bus, bool hint_priority, int64_t priority, sd r = bus_read_message(bus, hint_priority, priority); if (r < 0) return r; - if (r == 0) + if (r == 0) { + *m = NULL; return ret; + } ret = 1; } @@ -1874,7 +1929,7 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) { r = bus_write_message(bus, m, &idx); if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + if (ERRNO_IS_DISCONNECT(r)) { bus_enter_closing(bus); return -ECONNRESET; } @@ -1888,7 +1943,7 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) { * of the wqueue array is always allocated so * that we always can remember how much was * written. */ - bus->wqueue[0] = sd_bus_message_ref(m); + bus->wqueue[0] = bus_message_ref_queued(m, bus); bus->wqueue_size = 1; bus->windex = idx; } @@ -1902,7 +1957,7 @@ _public_ int sd_bus_send(sd_bus *bus, sd_bus_message *_m, uint64_t *cookie) { if (!GREEDY_REALLOC(bus->wqueue, bus->wqueue_allocated, bus->wqueue_size + 1)) return -ENOMEM; - bus->wqueue[bus->wqueue_size++] = sd_bus_message_ref(m); + bus->wqueue[bus->wqueue_size++] = bus_message_ref_queued(m, bus); } finish: @@ -2080,7 +2135,7 @@ _public_ int sd_bus_call( _cleanup_(sd_bus_message_unrefp) sd_bus_message *m = sd_bus_message_ref(_m); usec_t timeout; uint64_t cookie; - unsigned i; + size_t i; int r; bus_assert_return(m, -EINVAL, error); @@ -2122,37 +2177,30 @@ _public_ int sd_bus_call( usec_t left; while (i < bus->rqueue_size) { - sd_bus_message *incoming = NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message *incoming = NULL; - incoming = bus->rqueue[i]; + incoming = sd_bus_message_ref(bus->rqueue[i]); if (incoming->reply_cookie == cookie) { /* Found a match! */ - memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); - bus->rqueue_size--; + rqueue_drop_one(bus, i); log_debug_bus_message(incoming); if (incoming->header->type == SD_BUS_MESSAGE_METHOD_RETURN) { if (incoming->n_fds <= 0 || bus->accept_fd) { if (reply) - *reply = incoming; - else - sd_bus_message_unref(incoming); + *reply = TAKE_PTR(incoming); return 1; } - r = sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry."); - sd_bus_message_unref(incoming); - return r; + return sd_bus_error_setf(error, SD_BUS_ERROR_INCONSISTENT_MESSAGE, "Reply message contained file descriptors which I couldn't accept. Sorry."); - } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) { - r = sd_bus_error_copy(error, &incoming->error); - sd_bus_message_unref(incoming); - return r; - } else { + } else if (incoming->header->type == SD_BUS_MESSAGE_METHOD_ERROR) + return sd_bus_error_copy(error, &incoming->error); + else { r = -EIO; goto fail; } @@ -2162,15 +2210,11 @@ _public_ int sd_bus_call( incoming->sender && streq(bus->unique_name, incoming->sender)) { - memmove(bus->rqueue + i, bus->rqueue + i + 1, sizeof(sd_bus_message*) * (bus->rqueue_size - i - 1)); - bus->rqueue_size--; + rqueue_drop_one(bus, i); - /* Our own message? Somebody is trying - * to send its own client a message, - * let's not dead-lock, let's fail - * immediately. */ + /* Our own message? Somebody is trying to send its own client a message, + * let's not dead-lock, let's fail immediately. */ - sd_bus_message_unref(incoming); r = -ELOOP; goto fail; } @@ -2181,7 +2225,7 @@ _public_ int sd_bus_call( r = bus_read_message(bus, false, 0); if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + if (ERRNO_IS_DISCONNECT(r)) { bus_enter_closing(bus); r = -ECONNRESET; } @@ -2214,7 +2258,7 @@ _public_ int sd_bus_call( r = dispatch_wqueue(bus); if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + if (ERRNO_IS_DISCONNECT(r)) { bus_enter_closing(bus); r = -ECONNRESET; } @@ -2628,7 +2672,6 @@ static int process_builtin(sd_bus *bus, sd_bus_message *m) { SD_BUS_ERROR_UNKNOWN_METHOD, "Unknown method '%s' on interface '%s'.", m->member, m->interface); } - if (r < 0) return r; @@ -2752,7 +2795,6 @@ static int process_running(sd_bus *bus, bool hint_priority, int64_t priority, sd return r; *ret = TAKE_PTR(m); - return 1; } @@ -2974,7 +3016,7 @@ static int bus_process_internal(sd_bus *bus, bool hint_priority, int64_t priorit assert_not_reached("Unknown state"); } - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + if (ERRNO_IS_DISCONNECT(r)) { bus_enter_closing(bus); r = 1; } else if (r < 0) @@ -3105,7 +3147,7 @@ _public_ int sd_bus_flush(sd_bus *bus) { for (;;) { r = dispatch_wqueue(bus); if (r < 0) { - if (IN_SET(r, -ENOTCONN, -ECONNRESET, -EPIPE, -ESHUTDOWN)) { + if (ERRNO_IS_DISCONNECT(r)) { bus_enter_closing(bus); return -ECONNRESET; } diff --git a/src/libsystemd/sd-bus/test-bus-address.c b/src/libsystemd/sd-bus/test-bus-address.c index db5ff72ef..70a6df10f 100644 --- a/src/libsystemd/sd-bus/test-bus-address.c +++ b/src/libsystemd/sd-bus/test-bus-address.c @@ -4,6 +4,7 @@ #include "log.h" #include "string-util.h" #include "strv.h" +#include "tests.h" static void test_one_address(sd_bus *b, const char *host, @@ -40,8 +41,8 @@ static void test_bus_set_address_system_remote(char **args) { -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, "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]", @@ -59,9 +60,7 @@ static void test_bus_set_address_system_remote(char **args) { } int main(int argc, char *argv[]) { - log_set_max_level(LOG_INFO); - log_parse_environment(); - log_open(); + test_setup_logging(LOG_INFO); test_bus_set_address_system_remote(argv + 1); diff --git a/src/libsystemd/sd-bus/test-bus-benchmark.c b/src/libsystemd/sd-bus/test-bus-benchmark.c index 2dd3d41a3..8de0a859e 100644 --- a/src/libsystemd/sd-bus/test-bus-benchmark.c +++ b/src/libsystemd/sd-bus/test-bus-benchmark.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "sd-bus.h" diff --git a/src/libsystemd/sd-bus/test-bus-cleanup.c b/src/libsystemd/sd-bus/test-bus-cleanup.c index bea722ba0..99d335e3f 100644 --- a/src/libsystemd/sd-bus/test-bus-cleanup.c +++ b/src/libsystemd/sd-bus/test-bus-cleanup.c @@ -7,7 +7,6 @@ #include "bus-internal.h" #include "bus-message.h" #include "bus-util.h" -#include "refcnt.h" #include "tests.h" static bool use_system_bus = false; @@ -16,7 +15,7 @@ static void test_bus_new(void) { _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; assert_se(sd_bus_new(&bus) == 0); - printf("after new: refcount %u\n", REFCNT_GET(bus->n_ref)); + assert_se(bus->n_ref == 1); } static int test_bus_open(void) { @@ -32,7 +31,7 @@ static int test_bus_open(void) { } assert_se(r >= 0); - printf("after open: refcount %u\n", REFCNT_GET(bus->n_ref)); + assert_se(bus->n_ref >= 1); /* we send a hello message when opening, so the count is above 1 */ return 0; } @@ -45,10 +44,10 @@ static void test_bus_new_method_call(void) { assert_se(sd_bus_message_new_method_call(bus, &m, "a.service.name", "/an/object/path", "an.interface.name", "AMethodName") >= 0); - printf("after message_new_method_call: refcount %u\n", REFCNT_GET(bus->n_ref)); - + assert_se(m->n_ref == 1); /* We hold the only reference to the message */ + assert_se(bus->n_ref >= 2); sd_bus_flush_close_unref(bus); - printf("after bus_flush_close_unref: refcount %u\n", m->n_ref); + assert_se(m->n_ref == 1); } static void test_bus_new_signal(void) { @@ -59,10 +58,10 @@ static void test_bus_new_signal(void) { assert_se(sd_bus_message_new_signal(bus, &m, "/an/object/path", "an.interface.name", "Name") >= 0); - printf("after message_new_signal: refcount %u\n", REFCNT_GET(bus->n_ref)); - + assert_se(m->n_ref == 1); /* We hold the only reference to the message */ + assert_se(bus->n_ref >= 2); sd_bus_flush_close_unref(bus); - printf("after bus_flush_close_unref: refcount %u\n", m->n_ref); + assert_se(m->n_ref == 1); } int main(int argc, char **argv) { diff --git a/src/libsystemd/sd-bus/test-bus-introspect.c b/src/libsystemd/sd-bus/test-bus-introspect.c index 940dbfe0e..9c8e93e89 100644 --- a/src/libsystemd/sd-bus/test-bus-introspect.c +++ b/src/libsystemd/sd-bus/test-bus-introspect.c @@ -2,6 +2,7 @@ #include "bus-introspect.h" #include "log.h" +#include "tests.h" static int prop_get(sd_bus *bus, const char *path, const char *interface, const char *property, sd_bus_message *reply, void *userdata, sd_bus_error *error) { return -EINVAL; @@ -29,7 +30,7 @@ static const sd_bus_vtable vtable[] = { int main(int argc, char *argv[]) { struct introspect intro; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); assert_se(introspect_begin(&intro, false) >= 0); diff --git a/src/libsystemd/sd-bus/test-bus-match.c b/src/libsystemd/sd-bus/test-bus-match.c index 0949d8dee..47014c403 100644 --- a/src/libsystemd/sd-bus/test-bus-match.c +++ b/src/libsystemd/sd-bus/test-bus-match.c @@ -6,6 +6,7 @@ #include "bus-util.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "tests.h" static bool mask[32]; diff --git a/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c b/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c new file mode 100644 index 000000000..f6506fb93 --- /dev/null +++ b/src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#include "sd-bus.h" + +#include "main-func.h" +#include "tests.h" + +static int test_ref_unref(void) { + sd_bus_message *m = NULL; + sd_bus *bus = NULL; + int r; + + /* This test will result in a memory leak in <= v240, but not on v241. Hence to be really useful it + * should be run through a leak tracker such as valgrind. */ + + r = sd_bus_open_system(&bus); + if (r < 0) + return log_tests_skipped("Failed to connect to bus"); + + /* Create a message and enqueue it (this shouldn't send it though as the connection setup is not complete yet) */ + assert_se(sd_bus_message_new_method_call(bus, &m, "foo.bar", "/foo", "quux.quux", "waldo") >= 0); + assert_se(sd_bus_send(bus, m, NULL) >= 0); + + /* Let's now unref the message first and the bus second. */ + m = sd_bus_message_unref(m); + bus = sd_bus_unref(bus); + + /* We should have a memory leak now on <= v240. Let's do this again, but destory in the opposite + * order. On v240 that too should be a leak. */ + + r = sd_bus_open_system(&bus); + if (r < 0) + return log_tests_skipped("Failed to connect to bus"); + + assert_se(sd_bus_message_new_method_call(bus, &m, "foo.bar", "/foo", "quux.quux", "waldo") >= 0); + assert_se(sd_bus_send(bus, m, NULL) >= 0); + + /* Let's now unref things in the opposite order */ + bus = sd_bus_unref(bus); + m = sd_bus_message_unref(m); + + return 0; +} + +static int run(int argc, char *argv[]) { + int r; + + test_setup_logging(LOG_INFO); + + r = test_ref_unref(); + if (r < 0) + return r; + + return 0; +} + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/libsystemd/sd-bus/test-bus-server.c b/src/libsystemd/sd-bus/test-bus-server.c index 0f1b9645b..e38bcdcc7 100644 --- a/src/libsystemd/sd-bus/test-bus-server.c +++ b/src/libsystemd/sd-bus/test-bus-server.c @@ -9,7 +9,7 @@ #include "bus-util.h" #include "log.h" #include "macro.h" -#include "util.h" +#include "memory-util.h" struct context { int fds[2]; diff --git a/src/libsystemd/sd-bus/test-bus-vtable.c b/src/libsystemd/sd-bus/test-bus-vtable.c index fd9ad8121..b278094fa 100644 --- a/src/libsystemd/sd-bus/test-bus-vtable.c +++ b/src/libsystemd/sd-bus/test-bus-vtable.c @@ -39,6 +39,10 @@ static const sd_bus_vtable vtable[] = { SD_BUS_METHOD("Exit", "", "", handler, 0), SD_BUS_METHOD_WITH_OFFSET("AlterSomething2", "s", "s", handler, 200, 0), SD_BUS_METHOD_WITH_OFFSET("Exit2", "", "", handler, 200, 0), + SD_BUS_METHOD_WITH_NAMES_OFFSET("AlterSomething3", "so", SD_BUS_PARAM(string) SD_BUS_PARAM(path), + "s", SD_BUS_PARAM(returnstring), handler, 200, 0), + SD_BUS_METHOD_WITH_NAMES("Exit3", "bx", SD_BUS_PARAM(with_confirmation) SD_BUS_PARAM(after_msec), + "bb", SD_BUS_PARAM(accepted) SD_BUS_PARAM(scheduled), handler, 0), SD_BUS_PROPERTY("Value", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE), SD_BUS_PROPERTY("Value2", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_EMITS_INVALIDATION), SD_BUS_PROPERTY("Value3", "s", value_handler, 10, SD_BUS_VTABLE_PROPERTY_CONST), @@ -53,9 +57,68 @@ static const sd_bus_vtable vtable[] = { SD_BUS_METHOD("NoOperation", NULL, NULL, NULL, 0), SD_BUS_SIGNAL("DummySignal", "b", 0), SD_BUS_SIGNAL("DummySignal2", "so", 0), + SD_BUS_SIGNAL_WITH_NAMES("DummySignal3", "so", SD_BUS_PARAM(string) SD_BUS_PARAM(path), 0), SD_BUS_VTABLE_END }; +struct sd_bus_vtable_original { + uint8_t type:8; + uint64_t flags:56; + union { + struct { + size_t element_size; + } start; + struct { + const char *member; + const char *signature; + const char *result; + sd_bus_message_handler_t handler; + size_t offset; + } method; + struct { + const char *member; + const char *signature; + } signal; + struct { + const char *member; + const char *signature; + sd_bus_property_get_t get; + sd_bus_property_set_t set; + size_t offset; + } property; + } x; +}; + +static const struct sd_bus_vtable_original vtable_format_original[] = { + { + .type = _SD_BUS_VTABLE_START, + .flags = 0, + .x = { + .start = { + .element_size = sizeof(struct sd_bus_vtable_original) + }, + }, + }, + { + .type = _SD_BUS_VTABLE_METHOD, + .flags = 0, + .x = { + .method = { + .member = "Exit", + .signature = "", + .result = "", + .handler = handler, + .offset = 0, + }, + }, + }, + { + .type = _SD_BUS_VTABLE_END, + .flags = 0, + .x = { { 0 } }, + } +}; + static void test_vtable(void) { sd_bus *bus = NULL; struct context c = {}; @@ -65,6 +128,8 @@ static void test_vtable(void) { assert(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.testVtable", vtable, &c) >= 0); assert(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.testVtable2", vtable, &c) >= 0); + /* the cast on the line below is needed to test with the old version of the table */ + assert(sd_bus_add_object_vtable(bus, NULL, "/foo", "org.freedesktop.systemd.testVtableOriginal", (const sd_bus_vtable *)vtable_format_original, &c) >= 0); assert(sd_bus_set_address(bus, DEFAULT_BUS_PATH) >= 0); r = sd_bus_start(bus); diff --git a/src/libsystemd/sd-bus/test-bus-watch-bind.c b/src/libsystemd/sd-bus/test-bus-watch-bind.c index 4b3da3007..2723866e0 100644 --- a/src/libsystemd/sd-bus/test-bus-watch-bind.c +++ b/src/libsystemd/sd-bus/test-bus-watch-bind.c @@ -16,6 +16,7 @@ #include "socket-util.h" #include "string-util.h" #include "tmpfile-util.h" +#include "tests.h" static int method_foobar(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) { log_info("Got Foobar() call."); @@ -198,7 +199,7 @@ int main(int argc, char *argv[]) { pthread_t server, client1, client2; char *path; - log_set_max_level(LOG_DEBUG); + test_setup_logging(LOG_DEBUG); /* We use /dev/shm here rather than /tmp, since some weird distros might set up /tmp as some weird fs that * doesn't support inotify properly. */ diff --git a/src/libsystemd/sd-device/device-enumerator-private.h b/src/libsystemd/sd-device/device-enumerator-private.h index 87411bfdd..cf2b26148 100644 --- a/src/libsystemd/sd-device/device-enumerator-private.h +++ b/src/libsystemd/sd-device/device-enumerator-private.h @@ -7,6 +7,7 @@ int device_enumerator_scan_devices(sd_device_enumerator *enumeartor); int device_enumerator_scan_subsystems(sd_device_enumerator *enumeartor); int device_enumerator_add_device(sd_device_enumerator *enumerator, sd_device *device); int device_enumerator_add_match_is_initialized(sd_device_enumerator *enumerator); +int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent); 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); diff --git a/src/libsystemd/sd-device/device-enumerator.c b/src/libsystemd/sd-device/device-enumerator.c index 20529aafd..435786017 100644 --- a/src/libsystemd/sd-device/device-enumerator.c +++ b/src/libsystemd/sd-device/device-enumerator.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "sd-device.h" #include "alloc-util.h" @@ -8,9 +11,9 @@ #include "dirent-util.h" #include "fd-util.h" #include "set.h" +#include "sort-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" #define DEVICE_ENUMERATE_MAX_DEPTH 256 @@ -36,7 +39,7 @@ struct sd_device_enumerator { Hashmap *match_property; Set *match_sysname; Set *match_tag; - sd_device *match_parent; + Set *match_parent; bool match_allow_uninitialized; }; @@ -75,7 +78,7 @@ static sd_device_enumerator *device_enumerator_free(sd_device_enumerator *enumer 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); + set_free_free(enumerator->match_parent); return mfree(enumerator); } @@ -217,18 +220,42 @@ _public_ int sd_device_enumerator_add_match_tag(sd_device_enumerator *enumerator return 0; } -_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { +static void device_enumerator_clear_match_parent(sd_device_enumerator *enumerator) { + if (!enumerator) + return; + + set_clear_free(enumerator->match_parent); +} + +int device_enumerator_add_match_parent_incremental(sd_device_enumerator *enumerator, sd_device *parent) { + const char *path; + int r; + assert_return(enumerator, -EINVAL); assert_return(parent, -EINVAL); - sd_device_unref(enumerator->match_parent); - enumerator->match_parent = sd_device_ref(parent); + r = sd_device_get_syspath(parent, &path); + if (r < 0) + return r; + + r = set_ensure_allocated(&enumerator->match_parent, NULL); + if (r < 0) + return r; + + r = set_put_strdup(enumerator->match_parent, path); + if (r < 0) + return r; enumerator->scan_uptodate = false; return 0; } +_public_ int sd_device_enumerator_add_match_parent(sd_device_enumerator *enumerator, sd_device *parent) { + device_enumerator_clear_match_parent(enumerator); + return device_enumerator_add_match_parent_incremental(enumerator, parent); +} + _public_ int sd_device_enumerator_allow_uninitialized(sd_device_enumerator *enumerator) { assert_return(enumerator, -EINVAL); @@ -399,22 +426,22 @@ static bool match_tag(sd_device_enumerator *enumerator, sd_device *device) { } static bool match_parent(sd_device_enumerator *enumerator, sd_device *device) { - const char *devpath, *devpath_dev; - int r; + const char *syspath_parent, *syspath; + Iterator i; assert(enumerator); assert(device); - if (!enumerator->match_parent) + if (set_isempty(enumerator->match_parent)) return true; - r = sd_device_get_devpath(enumerator->match_parent, &devpath); - assert(r >= 0); + assert_se(sd_device_get_syspath(device, &syspath) >= 0); - r = sd_device_get_devpath(device, &devpath_dev); - assert(r >= 0); + SET_FOREACH(syspath_parent, enumerator->match_parent, i) + if (path_startswith(syspath, syspath_parent)) + return true; - return startswith(devpath_dev, devpath); + return false; } static bool match_sysname(sd_device_enumerator *enumerator, const char *sysname) { @@ -745,18 +772,17 @@ static int parent_crawl_children(sd_device_enumerator *enumerator, const char *p static int enumerator_scan_devices_children(sd_device_enumerator *enumerator) { const char *path; int r = 0, k; + Iterator i; - r = sd_device_get_syspath(enumerator->match_parent, &path); - if (r < 0) - return r; + SET_FOREACH(path, enumerator->match_parent, i) { + k = parent_add_child(enumerator, path); + if (k < 0) + r = k; - k = parent_add_child(enumerator, path); - if (k < 0) - r = k; - - k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); - if (k < 0) - r = k; + k = parent_crawl_children(enumerator, path, DEVICE_ENUMERATE_MAX_DEPTH); + if (k < 0) + r = k; + } return r; } diff --git a/src/libsystemd/sd-device/device-internal.h b/src/libsystemd/sd-device/device-internal.h index 3ffca35cb..023fe0fcd 100644 --- a/src/libsystemd/sd-device/device-internal.h +++ b/src/libsystemd/sd-device/device-internal.h @@ -3,6 +3,7 @@ #include "sd-device.h" +#include "device-private.h" #include "hashmap.h" #include "set.h" #include "time-util.h" @@ -64,6 +65,10 @@ struct sd_device { uid_t devuid; gid_t devgid; + /* only set when device is passed through netlink */ + DeviceAction action; + uint64_t seqnum; + 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 */ @@ -81,19 +86,6 @@ struct sd_device { bool db_persist:1; /* don't clean up the db when switching from initrd to real root */ }; -typedef enum DeviceAction { - DEVICE_ACTION_ADD, - DEVICE_ACTION_REMOVE, - DEVICE_ACTION_CHANGE, - DEVICE_ACTION_MOVE, - DEVICE_ACTION_ONLINE, - DEVICE_ACTION_OFFLINE, - DEVICE_ACTION_BIND, - DEVICE_ACTION_UNBIND, - _DEVICE_ACTION_MAX, - _DEVICE_ACTION_INVALID = -1, -} DeviceAction; - int device_new_aux(sd_device **ret); int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db); int device_add_property_internal(sd_device *device, const char *key, const char *value); @@ -108,6 +100,3 @@ 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, 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-private.c b/src/libsystemd/sd-device/device-private.c index 76267a1e7..a0855001e 100644 --- a/src/libsystemd/sd-device/device-private.c +++ b/src/libsystemd/sd-device/device-private.c @@ -16,9 +16,9 @@ #include "hashmap.h" #include "macro.h" #include "mkdir.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" -#include "refcnt.h" #include "set.h" #include "string-table.h" #include "string-util.h" @@ -26,7 +26,6 @@ #include "strxcpyx.h" #include "tmpfile-util.h" #include "user-util.h" -#include "util.h" int device_add_property(sd_device *device, const char *key, const char *value) { int r; @@ -184,6 +183,72 @@ static int device_set_devgid(sd_device *device, const char *gid) { return 0; } +int device_get_action(sd_device *device, DeviceAction *action) { + assert(device); + + if (device->action < 0) + return -ENOENT; + + if (action) + *action = device->action; + + return 0; +} + +static int device_set_action(sd_device *device, const char *action) { + DeviceAction a; + int r; + + assert(device); + assert(action); + + a = device_action_from_string(action); + if (a < 0) + return -EINVAL; + + r = device_add_property_internal(device, "ACTION", action); + if (r < 0) + return r; + + device->action = a; + + return 0; +} + +int device_get_seqnum(sd_device *device, uint64_t *seqnum) { + assert(device); + + if (device->seqnum == 0) + return -ENOENT; + + if (seqnum) + *seqnum = device->seqnum; + + return 0; +} + +static int device_set_seqnum(sd_device *device, const char *str) { + uint64_t seqnum; + int r; + + assert(device); + assert(str); + + r = safe_atou64(str, &seqnum); + if (r < 0) + return r; + if (seqnum == 0) + return -EINVAL; + + r = device_add_property_internal(device, "SEQNUM", str); + if (r < 0) + return r; + + device->seqnum = seqnum; + + return 0; +} + static int device_amend(sd_device *device, const char *key, const char *value) { int r; @@ -242,6 +307,14 @@ static int device_amend(sd_device *device, const char *key, const char *value) { r = device_set_devgid(device, value); if (r < 0) return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value); + } else if (streq(key, "ACTION")) { + r = device_set_action(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set action to '%s': %m", value); + } else if (streq(key, "SEQNUM")) { + r = device_set_seqnum(device, value); + if (r < 0) + return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value); } else if (streq(key, "DEVLINKS")) { const char *word, *state; size_t l; @@ -279,23 +352,7 @@ static int device_amend(sd_device *device, const char *key, const char *value) { return 0; } -static const char* const device_action_table[_DEVICE_ACTION_MAX] = { - [DEVICE_ACTION_ADD] = "add", - [DEVICE_ACTION_REMOVE] = "remove", - [DEVICE_ACTION_CHANGE] = "change", - [DEVICE_ACTION_MOVE] = "move", - [DEVICE_ACTION_ONLINE] = "online", - [DEVICE_ACTION_OFFLINE] = "offline", - [DEVICE_ACTION_BIND] = "bind", - [DEVICE_ACTION_UNBIND] = "unbind", -}; - -DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); - -static int device_append(sd_device *device, char *key, const char **_major, const char **_minor, uint64_t *_seqnum, - DeviceAction *_action) { - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum = 0; +static int device_append(sd_device *device, char *key, const char **_major, const char **_minor) { const char *major = NULL, *minor = NULL; char *value; int r; @@ -304,8 +361,6 @@ static int device_append(sd_device *device, char *key, const char **_major, cons assert(key); assert(_major); assert(_minor); - assert(_seqnum); - assert(_action); value = strchr(key, '='); if (!value) { @@ -322,19 +377,6 @@ static int device_append(sd_device *device, char *key, const char **_major, cons else if (streq(key, "MINOR")) minor = value; else { - if (streq(key, "ACTION")) { - action = device_action_from_string(value); - if (action == _DEVICE_ACTION_INVALID) - return -EINVAL; - } else if (streq(key, "SEQNUM")) { - r = safe_atou64(value, &seqnum); - if (r < 0) - return r; - else if (seqnum == 0) - /* kernel only sends seqnum > 0 */ - return -EINVAL; - } - r = device_amend(device, key, value); if (r < 0) return r; @@ -346,12 +388,6 @@ static int device_append(sd_device *device, char *key, const char **_major, cons if (minor != 0) *_minor = minor; - if (action != _DEVICE_ACTION_INVALID) - *_action = action; - - if (seqnum > 0) - *_seqnum = seqnum; - return 0; } @@ -361,10 +397,10 @@ void device_seal(sd_device *device) { device->sealed = true; } -static int device_verify(sd_device *device, DeviceAction action, uint64_t seqnum) { +static int device_verify(sd_device *device) { assert(device); - if (!device->devpath || !device->subsystem || action == _DEVICE_ACTION_INVALID || seqnum == 0) { + if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0) { log_device_debug(device, "sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum."); return -EINVAL; } @@ -378,8 +414,6 @@ int device_new_from_strv(sd_device **ret, char **strv) { _cleanup_(sd_device_unrefp) sd_device *device = NULL; char **key; const char *major = NULL, *minor = NULL; - DeviceAction action = _DEVICE_ACTION_INVALID; - uint64_t seqnum = 0; int r; assert(ret); @@ -390,7 +424,7 @@ int device_new_from_strv(sd_device **ret, char **strv) { return r; STRV_FOREACH(key, strv) { - r = device_append(device, *key, &major, &minor, &seqnum, &action); + r = device_append(device, *key, &major, &minor); if (r < 0) return r; } @@ -401,7 +435,7 @@ int device_new_from_strv(sd_device **ret, char **strv) { return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor); } - r = device_verify(device, action, seqnum); + r = device_verify(device); if (r < 0) return r; @@ -413,8 +447,6 @@ int device_new_from_strv(sd_device **ret, char **strv) { 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 = 0; unsigned i = 0; int r; @@ -438,7 +470,7 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { } i += end - key + 1; - r = device_append(device, key, &major, &minor, &seqnum, &action); + r = device_append(device, key, &major, &minor); if (r < 0) return r; } @@ -449,7 +481,7 @@ int device_new_from_nulstr(sd_device **ret, uint8_t *nulstr, size_t len) { return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor); } - r = device_verify(device, action, seqnum); + r = device_verify(device); if (r < 0) return r; @@ -675,7 +707,7 @@ int device_new_from_synthetic_event(sd_device **new_device, const char *syspath, if (r < 0) return r; - r = device_add_property_internal(ret, "ACTION", action); + r = device_set_action(ret, action); if (r < 0) return r; @@ -954,3 +986,16 @@ int device_delete_db(sd_device *device) { return 0; } + +static const char* const device_action_table[_DEVICE_ACTION_MAX] = { + [DEVICE_ACTION_ADD] = "add", + [DEVICE_ACTION_REMOVE] = "remove", + [DEVICE_ACTION_CHANGE] = "change", + [DEVICE_ACTION_MOVE] = "move", + [DEVICE_ACTION_ONLINE] = "online", + [DEVICE_ACTION_OFFLINE] = "offline", + [DEVICE_ACTION_BIND] = "bind", + [DEVICE_ACTION_UNBIND] = "unbind", +}; + +DEFINE_STRING_TABLE_LOOKUP(device_action, DeviceAction); diff --git a/src/libsystemd/sd-device/device-private.h b/src/libsystemd/sd-device/device-private.h index 062bfd651..9696f9c02 100644 --- a/src/libsystemd/sd-device/device-private.h +++ b/src/libsystemd/sd-device/device-private.h @@ -8,6 +8,21 @@ #include "sd-device.h" +#include "macro.h" + +typedef enum DeviceAction { + DEVICE_ACTION_ADD, + DEVICE_ACTION_REMOVE, + DEVICE_ACTION_CHANGE, + DEVICE_ACTION_MOVE, + DEVICE_ACTION_ONLINE, + DEVICE_ACTION_OFFLINE, + DEVICE_ACTION_BIND, + DEVICE_ACTION_UNBIND, + _DEVICE_ACTION_MAX, + _DEVICE_ACTION_INVALID = -1, +} DeviceAction; + 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); @@ -19,6 +34,8 @@ int device_get_watch_handle(sd_device *device, int *handle); int device_get_devnode_mode(sd_device *device, mode_t *mode); int device_get_devnode_uid(sd_device *device, uid_t *uid); int device_get_devnode_gid(sd_device *device, gid_t *gid); +int device_get_action(sd_device *device, DeviceAction *action); +int device_get_seqnum(sd_device *device, uint64_t *seqnum); void device_seal(sd_device *device); void device_set_is_initialized(sd_device *device); @@ -50,7 +67,11 @@ 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_internal_filename(sd_device *device, const char *filename); /* For fuzzer */ 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); } + +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/sd-device.c b/src/libsystemd/sd-device/sd-device.c index 2a69f2e94..c2315c03f 100644 --- a/src/libsystemd/sd-device/sd-device.c +++ b/src/libsystemd/sd-device/sd-device.c @@ -43,6 +43,7 @@ int device_new_aux(sd_device **ret) { .devmode = (mode_t) -1, .devuid = (uid_t) -1, .devgid = (gid_t) -1, + .action = _DEVICE_ACTION_INVALID, }; *ret = device; @@ -236,7 +237,7 @@ _public_ int sd_device_new_from_devnum(sd_device **ret, char type, dev_t devnum) assert_return(IN_SET(type, 'b', 'c'), -EINVAL); /* use /sys/dev/{block,char}/: link */ - snprintf(id, sizeof(id), "%u:%u", major(devnum), minor(devnum)); + xsprintf(id, "%u:%u", major(devnum), minor(devnum)); syspath = strjoina("/sys/dev/", (type == 'b' ? "block" : "char"), "/", id); @@ -1110,6 +1111,7 @@ int device_add_devlink(sd_device *device, const char *devlink) { static int device_add_property_internal_from_string(sd_device *device, const char *str) { _cleanup_free_ char *key = NULL; char *value; + int r; assert(device); assert(str); @@ -1127,7 +1129,13 @@ static int device_add_property_internal_from_string(sd_device *device, const cha if (isempty(++value)) value = NULL; - return device_add_property_internal(device, key, value); + /* Add the property to both sd_device::properties and sd_device::properties_db, + * as this is called by only handle_db_line(). */ + r = device_add_property_aux(device, key, value, false); + if (r < 0) + return r; + + return device_add_property_aux(device, key, value, true); } int device_set_usec_initialized(sd_device *device, usec_t when) { @@ -1266,13 +1274,11 @@ int device_get_id_filename(sd_device *device, const char **ret) { return 0; } -int device_read_db_internal(sd_device *device, bool force) { +int device_read_db_internal_filename(sd_device *device, const char *filename) { _cleanup_free_ char *db = NULL; - char *path; - const char *id, *value; + const char *value; + size_t db_len, i; char key; - size_t db_len; - unsigned i; int r; enum { @@ -1284,22 +1290,14 @@ int device_read_db_internal(sd_device *device, bool force) { } state = PRE_KEY; assert(device); + assert(filename); - if (device->db_loaded || (!force && 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); + r = read_full_file(filename, &db, &db_len); if (r < 0) { if (r == -ENOENT) return 0; - else - return log_device_debug_errno(device, 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", filename); } /* devices with a database entry are initialized */ @@ -1352,13 +1350,31 @@ int device_read_db_internal(sd_device *device, bool force) { break; default: - assert_not_reached("Invalid state when parsing db"); + return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax."); } } return 0; } +int device_read_db_internal(sd_device *device, bool force) { + const char *id, *path; + int r; + + assert(device); + + if (device->db_loaded || (!force && device->sealed)) + return 0; + + r = device_get_id_filename(device, &id); + if (r < 0) + return r; + + path = strjoina("/run/udev/data/", id); + + return device_read_db_internal_filename(device, path); +} + _public_ int sd_device_get_is_initialized(sd_device *device) { int r; diff --git a/src/libsystemd/sd-device/test-sd-device.c b/src/libsystemd/sd-device/test-sd-device.c index e459b13b9..33a25d576 100644 --- a/src/libsystemd/sd-device/test-sd-device.c +++ b/src/libsystemd/sd-device/test-sd-device.c @@ -6,7 +6,7 @@ #include "hashmap.h" #include "string-util.h" #include "tests.h" -#include "util.h" +#include "time-util.h" static void test_sd_device_one(sd_device *d) { const char *syspath, *subsystem, *val; diff --git a/src/libsystemd/sd-event/sd-event.c b/src/libsystemd/sd-event/sd-event.c index 04ba7e257..1987f279e 100644 --- a/src/libsystemd/sd-event/sd-event.c +++ b/src/libsystemd/sd-event/sd-event.c @@ -15,6 +15,7 @@ #include "hashmap.h" #include "list.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "prioq.h" #include "process-util.h" @@ -23,7 +24,6 @@ #include "string-table.h" #include "string-util.h" #include "time-util.h" -#include "util.h" #define DEFAULT_ACCURACY_USEC (250 * USEC_PER_MSEC) @@ -3116,7 +3116,7 @@ _public_ int sd_event_wait(sd_event *e, uint64_t timeout) { timeout = 0; m = epoll_wait(e->epoll_fd, ev_queue, ev_queue_max, - timeout == (uint64_t) -1 ? -1 : (int) ((timeout + USEC_PER_MSEC - 1) / USEC_PER_MSEC)); + timeout == (uint64_t) -1 ? -1 : (int) DIV_ROUND_UP(timeout, USEC_PER_MSEC)); if (m < 0) { if (errno == EINTR) { e->state = SD_EVENT_PENDING; diff --git a/src/libsystemd/sd-hwdb/hwdb-util.c b/src/libsystemd/sd-hwdb/hwdb-util.c index f8529670b..c83575c7c 100644 --- a/src/libsystemd/sd-hwdb/hwdb-util.c +++ b/src/libsystemd/sd-hwdb/hwdb-util.c @@ -2,6 +2,7 @@ #include #include +#include #include "alloc-util.h" #include "conf-files.h" @@ -13,6 +14,7 @@ #include "label.h" #include "mkdir.h" #include "path-util.h" +#include "sort-util.h" #include "strbuf.h" #include "string-util.h" #include "strv.h" @@ -432,7 +434,7 @@ static int trie_store(struct trie *trie, const char *filename, bool compat) { error_fclose: r = -errno; fclose(t.f); - unlink(filename_tmp); + (void) unlink(filename_tmp); return r; } diff --git a/src/libsystemd/sd-hwdb/sd-hwdb.c b/src/libsystemd/sd-hwdb/sd-hwdb.c index 233944c07..79fe1a83a 100644 --- a/src/libsystemd/sd-hwdb/sd-hwdb.c +++ b/src/libsystemd/sd-hwdb/sd-hwdb.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "sd-hwdb.h" @@ -18,12 +19,12 @@ #include "hashmap.h" #include "hwdb-internal.h" #include "hwdb-util.h" -#include "refcnt.h" +#include "nulstr-util.h" #include "string-util.h" -#include "util.h" +#include "time-util.h" struct sd_hwdb { - RefCount n_ref; + unsigned n_ref; FILE *f; struct stat st; @@ -316,27 +317,27 @@ _public_ int sd_hwdb_new(sd_hwdb **ret) { if (!hwdb) return -ENOMEM; - hwdb->n_ref = REFCNT_INIT; + hwdb->n_ref = 1; /* find hwdb.bin in hwdb_bin_paths */ NULSTR_FOREACH(hwdb_bin_path, hwdb_bin_paths) { + log_debug("Trying to open \"%s\"...", hwdb_bin_path); hwdb->f = fopen(hwdb_bin_path, "re"); if (hwdb->f) break; - else if (errno == ENOENT) - continue; - else + if (errno != ENOENT) 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'"); - return -ENOENT; - } + if (!hwdb->f) + return log_debug_errno(SYNTHETIC_ERRNO(ENOENT), + "hwdb.bin does not exist, please run 'systemd-hwdb update'"); - 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, "Failed to read %s: %m", hwdb_bin_path); + if (fstat(fileno(hwdb->f), &hwdb->st) < 0) + return log_debug_errno(errno, "Failed to stat %s: %m", hwdb_bin_path); + if (hwdb->st.st_size < (off_t) offsetof(struct trie_header_f, strings_len) + 8) + return log_debug_errno(SYNTHETIC_ERRNO(EIO), + "File %s is too short: %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) @@ -370,7 +371,7 @@ static sd_hwdb *hwdb_free(sd_hwdb *hwdb) { return mfree(hwdb); } -DEFINE_PUBLIC_ATOMIC_REF_UNREF_FUNC(sd_hwdb, sd_hwdb, hwdb_free) +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_hwdb, sd_hwdb, hwdb_free) bool hwdb_validate(sd_hwdb *hwdb) { bool found = false; @@ -383,12 +384,11 @@ bool hwdb_validate(sd_hwdb *hwdb) { return false; /* if hwdb.bin doesn't exist anywhere, we need to update */ - NULSTR_FOREACH(p, hwdb_bin_paths) { + NULSTR_FOREACH(p, hwdb_bin_paths) if (stat(p, &st) >= 0) { found = true; break; } - } if (!found) return true; diff --git a/src/libsystemd/sd-id128/sd-id128.c b/src/libsystemd/sd-id128/sd-id128.c index e72af1593..e5f82b886 100644 --- a/src/libsystemd/sd-id128/sd-id128.c +++ b/src/libsystemd/sd-id128/sd-id128.c @@ -117,7 +117,6 @@ _public_ int sd_id128_get_boot(sd_id128_t *ret) { } static int get_invocation_from_keyring(sd_id128_t *ret) { - _cleanup_free_ char *description = NULL; char *d, *p, *g, *u, *e; unsigned long perms; @@ -136,7 +135,7 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { if (key == -1) { /* Keyring support not available? No invocation key stored? */ if (IN_SET(errno, ENOSYS, ENOKEY)) - return 0; + return -ENXIO; return -errno; } @@ -212,7 +211,19 @@ static int get_invocation_from_keyring(sd_id128_t *ret) { if (c != sizeof(sd_id128_t)) return -EIO; - return 1; + return 0; +} + +static int get_invocation_from_environment(sd_id128_t *ret) { + const char *e; + + assert(ret); + + e = secure_getenv("INVOCATION_ID"); + if (!e) + return -ENXIO; + + return sd_id128_from_string(e, ret); } _public_ int sd_id128_get_invocation(sd_id128_t *ret) { @@ -222,31 +233,17 @@ _public_ int sd_id128_get_invocation(sd_id128_t *ret) { assert_return(ret, -EINVAL); if (sd_id128_is_null(saved_invocation_id)) { + /* We first check the environment. The environment variable is primarily relevant for user + * services, and sufficiently safe as long as no privilege boundary is involved. */ + r = get_invocation_from_environment(&saved_invocation_id); + if (r < 0 && r != -ENXIO) + return r; - /* We first try to read the invocation ID from the kernel keyring. This has the benefit that it is not - * fakeable by unprivileged code. If the information is not available in the keyring, we use - * $INVOCATION_ID but ignore the data if our process was called by less privileged code - * (i.e. secure_getenv() instead of getenv()). - * - * The kernel keyring is only relevant for system services (as for user services we don't store the - * invocation ID in the keyring, as there'd be no trust benefit in that). The environment variable is - * primarily relevant for user services, and sufficiently safe as no privilege boundary is involved. */ - + /* The kernel keyring is relevant for system services (as for user services we don't store + * the invocation ID in the keyring, as there'd be no trust benefit in that). */ r = get_invocation_from_keyring(&saved_invocation_id); if (r < 0) return r; - - if (r == 0) { - const char *e; - - e = secure_getenv("INVOCATION_ID"); - if (!e) - return -ENXIO; - - r = sd_id128_from_string(e, &saved_invocation_id); - if (r < 0) - return r; - } } *ret = saved_invocation_id; diff --git a/src/libsystemd/sd-login/sd-login.c b/src/libsystemd/sd-login/sd-login.c index 07f21e84d..0dc368e6e 100644 --- a/src/libsystemd/sd-login/sd-login.c +++ b/src/libsystemd/sd-login/sd-login.c @@ -818,7 +818,7 @@ _public_ int sd_get_uids(uid_t **users) { uid_t *t; n = MAX(16, 2*r); - t = realloc(l, sizeof(uid_t) * n); + t = reallocarray(l, sizeof(uid_t), n); if (!t) return -ENOMEM; diff --git a/src/libsystemd/sd-login/test-login.c b/src/libsystemd/sd-login/test-login.c index 1789e547b..e9df5f8bc 100644 --- a/src/libsystemd/sd-login/test-login.c +++ b/src/libsystemd/sd-login/test-login.c @@ -11,6 +11,7 @@ #include "log.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "util.h" static char* format_uids(char **buf, uid_t* uids, int count) { diff --git a/src/libsystemd/sd-netlink/generic-netlink.c b/src/libsystemd/sd-netlink/generic-netlink.c index 3445757da..384072e88 100644 --- a/src/libsystemd/sd-netlink/generic-netlink.c +++ b/src/libsystemd/sd-netlink/generic-netlink.c @@ -10,9 +10,10 @@ typedef struct { } genl_family; static const genl_family genl_families[] = { - [SD_GENL_ID_CTRL] = { .name = "", .version = 1 }, + [SD_GENL_ID_CTRL] = { .name = "", .version = 1 }, [SD_GENL_WIREGUARD] = { .name = "wireguard", .version = 1 }, [SD_GENL_FOU] = { .name = "fou", .version = 1 }, + [SD_GENL_L2TP] = { .name = "l2tp", .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 5c37279bd..751144539 100644 --- a/src/libsystemd/sd-netlink/local-addresses.c +++ b/src/libsystemd/sd-netlink/local-addresses.c @@ -6,6 +6,7 @@ #include "local-addresses.h" #include "macro.h" #include "netlink-util.h" +#include "sort-util.h" static int address_compare(const struct local_address *a, const struct local_address *b) { int r; diff --git a/src/libsystemd/sd-netlink/netlink-internal.h b/src/libsystemd/sd-netlink/netlink-internal.h index 98de4f0a8..4a366d421 100644 --- a/src/libsystemd/sd-netlink/netlink-internal.h +++ b/src/libsystemd/sd-netlink/netlink-internal.h @@ -8,7 +8,7 @@ #include "list.h" #include "netlink-types.h" #include "prioq.h" -#include "refcnt.h" +#include "time-util.h" #define RTNL_DEFAULT_TIMEOUT ((usec_t) (25 * USEC_PER_SEC)) @@ -56,7 +56,7 @@ struct sd_netlink_slot { }; struct sd_netlink { - RefCount n_ref; + unsigned n_ref; int fd; @@ -114,7 +114,7 @@ struct netlink_container { }; struct sd_netlink_message { - RefCount n_ref; + unsigned n_ref; sd_netlink *rtnl; diff --git a/src/libsystemd/sd-netlink/netlink-message.c b/src/libsystemd/sd-netlink/netlink-message.c index 5e9bc4513..0dcc53be5 100644 --- a/src/libsystemd/sd-netlink/netlink-message.c +++ b/src/libsystemd/sd-netlink/netlink-message.c @@ -12,9 +12,8 @@ #include "netlink-internal.h" #include "netlink-types.h" #include "netlink-util.h" -#include "refcnt.h" #include "socket-util.h" -#include "util.h" +#include "memory-util.h" #define GET_CONTAINER(m, i) ((i) < (m)->n_containers ? (struct rtattr*)((uint8_t*)(m)->hdr + (m)->containers[i].offset) : NULL) #define PUSH_CONTAINER(m, new) (m)->container_offsets[(m)->n_containers++] = (uint8_t*)(new) - (uint8_t*)(m)->hdr; @@ -36,7 +35,7 @@ int message_new_empty(sd_netlink *rtnl, sd_netlink_message **ret) { if (!m) return -ENOMEM; - m->n_ref = REFCNT_INIT; + m->n_ref = 1; m->protocol = rtnl->protocol; m->sealed = false; @@ -96,12 +95,10 @@ int sd_netlink_message_request_dump(sd_netlink_message *m, int dump) { return 0; } -DEFINE_ATOMIC_REF_FUNC(sd_netlink_message, sd_netlink_message); +DEFINE_TRIVIAL_REF_FUNC(sd_netlink_message, sd_netlink_message); sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { - sd_netlink_message *t; - - while (m && REFCNT_DEC(m->n_ref) == 0) { + while (m && --m->n_ref == 0) { unsigned i; free(m->hdr); @@ -109,7 +106,7 @@ sd_netlink_message *sd_netlink_message_unref(sd_netlink_message *m) { for (i = 0; i <= m->n_containers; i++) free(m->containers[i].attributes); - t = m; + sd_netlink_message *t = m; m = m->next; free(t); } @@ -334,36 +331,46 @@ int sd_netlink_message_append_data(sd_netlink_message *m, unsigned short type, c return 0; } -int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { +int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); + assert_return(IN_SET(family, AF_INET, AF_INET6), -EINVAL); r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); if (r < 0) return r; - r = add_rtattr(m, type, data, sizeof(struct in_addr)); + r = add_rtattr(m, type, data, FAMILY_ADDRESS_SIZE(family)); if (r < 0) return r; return 0; } +int sd_netlink_message_append_in_addr(sd_netlink_message *m, unsigned short type, const struct in_addr *data) { + return netlink_message_append_in_addr_union(m, type, AF_INET, (const union in_addr_union *) data); +} + int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short type, const struct in6_addr *data) { + return netlink_message_append_in_addr_union(m, type, AF_INET6, (const union in_addr_union *) data); +} + +int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data) { int r; assert_return(m, -EINVAL); assert_return(!m->sealed, -EPERM); assert_return(data, -EINVAL); + assert_return(IN_SET(data->sa.sa_family, AF_INET, AF_INET6), -EINVAL); - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_IN_ADDR); + r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR); if (r < 0) return r; - r = add_rtattr(m, type, data, sizeof(struct in6_addr)); + r = add_rtattr(m, type, data, data->sa.sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); if (r < 0) return r; @@ -371,41 +378,14 @@ int sd_netlink_message_append_in6_addr(sd_netlink_message *m, unsigned short typ } int sd_netlink_message_append_sockaddr_in(sd_netlink_message *m, unsigned short type, const struct sockaddr_in *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, sizeof(struct sockaddr_in)); - if (r < 0) - return r; - - return 0; + return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data); } int sd_netlink_message_append_sockaddr_in6(sd_netlink_message *m, unsigned short type, const struct sockaddr_in6 *data) { - int r; - - assert_return(m, -EINVAL); - assert_return(!m->sealed, -EPERM); - assert_return(data, -EINVAL); - - r = message_attribute_has_type(m, NULL, type, NETLINK_TYPE_SOCKADDR); - if (r < 0) - return r; - - r = add_rtattr(m, type, data, sizeof(struct sockaddr_in6)); - if (r < 0) - return r; - - return 0; + return netlink_message_append_sockaddr_union(m, type, (const union sockaddr_union *) data); } + int sd_netlink_message_append_ether_addr(sd_netlink_message *m, unsigned short type, const struct ether_addr *data) { int r; diff --git a/src/libsystemd/sd-netlink/netlink-socket.c b/src/libsystemd/sd-netlink/netlink-socket.c index 432e8e8c0..d1c95b260 100644 --- a/src/libsystemd/sd-netlink/netlink-socket.c +++ b/src/libsystemd/sd-netlink/netlink-socket.c @@ -14,7 +14,6 @@ #include "netlink-internal.h" #include "netlink-types.h" #include "netlink-util.h" -#include "refcnt.h" #include "socket-util.h" #include "util.h" diff --git a/src/libsystemd/sd-netlink/netlink-types.c b/src/libsystemd/sd-netlink/netlink-types.c index 9dcd3f2ac..8248ac0f5 100644 --- a/src/libsystemd/sd-netlink/netlink-types.c +++ b/src/libsystemd/sd-netlink/netlink-types.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #if HAVE_LINUX_FOU_H @@ -308,6 +309,7 @@ static const NLType rtnl_link_info_data_geneve_types[] = { static const NLType rtnl_link_info_data_can_types[] = { [IFLA_CAN_BITTIMING] = { .size = sizeof(struct can_bittiming) }, [IFLA_CAN_RESTART_MS] = { .type = NETLINK_TYPE_U32 }, + [IFLA_CAN_CTRLMODE] = { .size = sizeof(struct can_ctrlmode) }, }; /* these strings must match the .kind entries in the kernel */ @@ -651,19 +653,18 @@ static const NLType rtnl_routing_policy_rule_types[] = { [FRA_DST] = { .type = NETLINK_TYPE_IN_ADDR }, [FRA_SRC] = { .type = NETLINK_TYPE_IN_ADDR }, [FRA_IIFNAME] = { .type = NETLINK_TYPE_STRING }, - [RTA_OIF] = { .type = NETLINK_TYPE_U32 }, - [RTA_GATEWAY] = { .type = NETLINK_TYPE_IN_ADDR }, + [FRA_GOTO] = { .type = NETLINK_TYPE_U32 }, [FRA_PRIORITY] = { .type = NETLINK_TYPE_U32 }, [FRA_FWMARK] = { .type = NETLINK_TYPE_U32 }, [FRA_FLOW] = { .type = NETLINK_TYPE_U32 }, - [FRA_TUN_ID] = { .type = NETLINK_TYPE_U32 }, + [FRA_TUN_ID] = { .type = NETLINK_TYPE_U64 }, [FRA_SUPPRESS_IFGROUP] = { .type = NETLINK_TYPE_U32 }, [FRA_SUPPRESS_PREFIXLEN] = { .type = NETLINK_TYPE_U32 }, [FRA_TABLE] = { .type = NETLINK_TYPE_U32 }, [FRA_FWMASK] = { .type = NETLINK_TYPE_U32 }, [FRA_OIFNAME] = { .type = NETLINK_TYPE_STRING }, [FRA_PAD] = { .type = NETLINK_TYPE_U32 }, - [FRA_L3MDEV] = { .type = NETLINK_TYPE_U64 }, + [FRA_L3MDEV] = { .type = NETLINK_TYPE_U8 }, [FRA_UID_RANGE] = { .size = sizeof(struct fib_rule_uid_range) }, [FRA_PROTOCOL] = { .type = NETLINK_TYPE_U8 }, [FRA_IP_PROTO] = { .type = NETLINK_TYPE_U8 }, @@ -797,10 +798,62 @@ static const NLTypeSystem genl_fou_cmds_type_system = { .types = genl_fou_cmds, }; +static const NLType genl_l2tp_types[] = { + [L2TP_ATTR_PW_TYPE] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_ENCAP_TYPE] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_OFFSET] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_DATA_SEQ] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_L2SPEC_TYPE] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_L2SPEC_LEN] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_PROTO_VERSION] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_IFNAME] = { .type = NETLINK_TYPE_STRING }, + [L2TP_ATTR_CONN_ID] = { .type = NETLINK_TYPE_U32 }, + [L2TP_ATTR_PEER_CONN_ID] = { .type = NETLINK_TYPE_U32 }, + [L2TP_ATTR_SESSION_ID] = { .type = NETLINK_TYPE_U32 }, + [L2TP_ATTR_PEER_SESSION_ID] = { .type = NETLINK_TYPE_U32 }, + [L2TP_ATTR_UDP_CSUM] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_VLAN_ID] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_RECV_SEQ] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_SEND_SEQ] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_LNS_MODE] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_USING_IPSEC] = { .type = NETLINK_TYPE_U8 }, + [L2TP_ATTR_FD] = { .type = NETLINK_TYPE_U32 }, + [L2TP_ATTR_IP_SADDR] = { .type = NETLINK_TYPE_IN_ADDR }, + [L2TP_ATTR_IP_DADDR] = { .type = NETLINK_TYPE_IN_ADDR }, + [L2TP_ATTR_UDP_SPORT] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_UDP_DPORT] = { .type = NETLINK_TYPE_U16 }, + [L2TP_ATTR_IP6_SADDR] = { .type = NETLINK_TYPE_IN_ADDR }, + [L2TP_ATTR_IP6_DADDR] = { .type = NETLINK_TYPE_IN_ADDR }, + [L2TP_ATTR_UDP_ZERO_CSUM6_TX] = { .type = NETLINK_TYPE_FLAG }, + [L2TP_ATTR_UDP_ZERO_CSUM6_RX] = { .type = NETLINK_TYPE_FLAG }, +}; + +static const NLTypeSystem genl_l2tp_type_system = { + .count = ELEMENTSOF(genl_l2tp_types), + .types = genl_l2tp_types, +}; + +static const NLType genl_l2tp[] = { + [L2TP_CMD_TUNNEL_CREATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_TUNNEL_DELETE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_TUNNEL_MODIFY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_TUNNEL_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_SESSION_CREATE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_SESSION_DELETE] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_SESSION_MODIFY] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, + [L2TP_CMD_SESSION_GET] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_type_system }, +}; + +static const NLTypeSystem genl_l2tp_tunnel_session_type_system = { + .count = ELEMENTSOF(genl_l2tp), + .types = genl_l2tp, +}; + static const NLType genl_families[] = { [SD_GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_ctrl_id_ctrl_type_system }, [SD_GENL_WIREGUARD] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_wireguard_type_system }, [SD_GENL_FOU] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_fou_cmds_type_system}, + [SD_GENL_L2TP] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_l2tp_tunnel_session_type_system }, }; const NLTypeSystem genl_family_type_system_root = { @@ -809,6 +862,7 @@ const NLTypeSystem genl_family_type_system_root = { }; static const NLType genl_types[] = { + [NLMSG_ERROR] = { .type = NETLINK_TYPE_NESTED, .type_system = &empty_type_system, .size = sizeof(struct nlmsgerr) }, [GENL_ID_CTRL] = { .type = NETLINK_TYPE_NESTED, .type_system = &genl_get_family_type_system, .size = sizeof(struct genlmsghdr) }, }; diff --git a/src/libsystemd/sd-netlink/netlink-util.c b/src/libsystemd/sd-netlink/netlink-util.c index 3928dfbab..628ce507a 100644 --- a/src/libsystemd/sd-netlink/netlink-util.c +++ b/src/libsystemd/sd-netlink/netlink-util.c @@ -13,6 +13,9 @@ int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name) { assert(ifindex > 0); assert(name); + if (!ifname_valid(name)) + return -EINVAL; + if (!*rtnl) { r = sd_netlink_open(rtnl); if (r < 0) diff --git a/src/libsystemd/sd-netlink/netlink-util.h b/src/libsystemd/sd-netlink/netlink-util.h index d2723285a..0d01a4bd0 100644 --- a/src/libsystemd/sd-netlink/netlink-util.h +++ b/src/libsystemd/sd-netlink/netlink-util.h @@ -3,6 +3,8 @@ #include "sd-netlink.h" +#include "in-addr-util.h" +#include "socket-util.h" #include "util.h" int rtnl_message_new_synthetic_error(sd_netlink *rtnl, int error, uint32_t serial, sd_netlink_message **ret); @@ -58,3 +60,6 @@ int rtnl_log_create_error(int r); (sd_netlink_destroy_t) _destroy_, \ userdata, __func__); \ }) + +int netlink_message_append_in_addr_union(sd_netlink_message *m, unsigned short type, int family, const union in_addr_union *data); +int netlink_message_append_sockaddr_union(sd_netlink_message *m, unsigned short type, const union sockaddr_union *data); diff --git a/src/libsystemd/sd-netlink/rtnl-message.c b/src/libsystemd/sd-netlink/rtnl-message.c index 2d4d00e0e..ccc497de0 100644 --- a/src/libsystemd/sd-netlink/rtnl-message.c +++ b/src/libsystemd/sd-netlink/rtnl-message.c @@ -12,7 +12,6 @@ #include "netlink-internal.h" #include "netlink-types.h" #include "netlink-util.h" -#include "refcnt.h" #include "socket-util.h" #include "util.h" diff --git a/src/libsystemd/sd-netlink/sd-netlink.c b/src/libsystemd/sd-netlink/sd-netlink.c index d83952d0c..f3e267f00 100644 --- a/src/libsystemd/sd-netlink/sd-netlink.c +++ b/src/libsystemd/sd-netlink/sd-netlink.c @@ -28,7 +28,7 @@ static int sd_netlink_new(sd_netlink **ret) { return -ENOMEM; *rtnl = (sd_netlink) { - .n_ref = REFCNT_INIT, + .n_ref = 1, .fd = -1, .sockaddr.nl.nl_family = AF_NETLINK, .original_pid = getpid_cached(), @@ -182,7 +182,7 @@ static sd_netlink *netlink_free(sd_netlink *rtnl) { return mfree(rtnl); } -DEFINE_ATOMIC_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free); +DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free); static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) { assert(rtnl); diff --git a/src/libsystemd/sd-netlink/test-local-addresses.c b/src/libsystemd/sd-netlink/test-local-addresses.c index 17114265d..37505ea78 100644 --- a/src/libsystemd/sd-netlink/test-local-addresses.c +++ b/src/libsystemd/sd-netlink/test-local-addresses.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "af-list.h" #include "alloc-util.h" #include "in-addr-util.h" diff --git a/src/libsystemd/sd-network/network-util.c b/src/libsystemd/sd-network/network-util.c index df5ce8684..f46a3ff78 100644 --- a/src/libsystemd/sd-network/network-util.c +++ b/src/libsystemd/sd-network/network-util.c @@ -3,6 +3,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "network-util.h" +#include "string-table.h" #include "strv.h" bool network_is_online(void) { @@ -18,3 +19,16 @@ bool network_is_online(void) { return false; } + +static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = { + [LINK_OPERSTATE_OFF] = "off", + [LINK_OPERSTATE_NO_CARRIER] = "no-carrier", + [LINK_OPERSTATE_DORMANT] = "dormant", + [LINK_OPERSTATE_DEGRADED_CARRIER] = "degraded-carrier", + [LINK_OPERSTATE_CARRIER] = "carrier", + [LINK_OPERSTATE_DEGRADED] = "degraded", + [LINK_OPERSTATE_ENSLAVED] = "enslaved", + [LINK_OPERSTATE_ROUTABLE] = "routable", +}; + +DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState); diff --git a/src/libsystemd/sd-network/network-util.h b/src/libsystemd/sd-network/network-util.h index e868bc2e2..6936fd536 100644 --- a/src/libsystemd/sd-network/network-util.h +++ b/src/libsystemd/sd-network/network-util.h @@ -3,4 +3,22 @@ #include "sd-network.h" +#include "macro.h" + bool network_is_online(void); + +typedef enum LinkOperationalState { + LINK_OPERSTATE_OFF, + LINK_OPERSTATE_NO_CARRIER, + LINK_OPERSTATE_DORMANT, + LINK_OPERSTATE_DEGRADED_CARRIER, + LINK_OPERSTATE_CARRIER, + LINK_OPERSTATE_DEGRADED, + LINK_OPERSTATE_ENSLAVED, + LINK_OPERSTATE_ROUTABLE, + _LINK_OPERSTATE_MAX, + _LINK_OPERSTATE_INVALID = -1 +} LinkOperationalState; + +const char* link_operstate_to_string(LinkOperationalState s) _const_; +LinkOperationalState link_operstate_from_string(const char *s) _pure_; diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index 812826fe3..2d715043e 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -164,6 +164,27 @@ _public_ int sd_network_link_get_required_for_online(int ifindex) { return parse_boolean(s); } +_public_ int sd_network_link_get_required_operstate_for_online(int ifindex, char **state) { + _cleanup_free_ char *s = NULL; + int r; + + assert_return(state, -EINVAL); + + r = network_link_get_string(ifindex, "REQUIRED_OPER_STATE_FOR_ONLINE", &s); + if (r < 0) { + if (r != -ENODATA) + return r; + + /* For compatibility, assuming degraded. */ + s = strdup("degraded"); + if (!s) + return -ENOMEM; + } + + *state = TAKE_PTR(s); + return 0; +} + _public_ int sd_network_link_get_llmnr(int ifindex, char **llmnr) { return network_link_get_string(ifindex, "LLMNR", llmnr); } diff --git a/src/libsystemd/sd-resolve/sd-resolve.c b/src/libsystemd/sd-resolve/sd-resolve.c index 36b9c8d01..868cc023b 100644 --- a/src/libsystemd/sd-resolve/sd-resolve.c +++ b/src/libsystemd/sd-resolve/sd-resolve.c @@ -16,14 +16,15 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "errno-util.h" #include "fd-util.h" #include "io-util.h" #include "list.h" +#include "memory-util.h" #include "missing.h" +#include "process-util.h" #include "resolve-private.h" #include "socket-util.h" -#include "util.h" -#include "process-util.h" #define WORKERS_MIN 1U #define WORKERS_MAX 16U diff --git a/src/libudev/libudev-device.c b/src/libudev/libudev-device.c index 1e7d774ba..357adf696 100644 --- a/src/libudev/libudev-device.c +++ b/src/libudev/libudev-device.c @@ -45,24 +45,15 @@ * * 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) { - const char *seqnum; - unsigned long long ret; - int r; +_public_ unsigned long long udev_device_get_seqnum(struct udev_device *udev_device) { + uint64_t seqnum; assert_return_errno(udev_device, 0, EINVAL); - r = sd_device_get_property_value(udev_device->device, "SEQNUM", &seqnum); - if (r == -ENOENT) + if (device_get_seqnum(udev_device->device, &seqnum) < 0) return 0; - else if (r < 0) - return_with_errno(0, r); - r = safe_atollu(seqnum, &ret); - if (r < 0) - return_with_errno(0, r); - - return ret; + return seqnum; } /** @@ -652,18 +643,14 @@ _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; - int r; + DeviceAction action; assert_return_errno(udev_device, NULL, EINVAL); - r = sd_device_get_property_value(udev_device->device, "ACTION", &action); - if (r == -ENOENT) + if (device_get_action(udev_device->device, &action) < 0) return NULL; - if (r < 0) - return_with_errno(NULL, r); - return action; + return device_action_to_string(action); } /** diff --git a/src/libudev/libudev-enumerate.c b/src/libudev/libudev-enumerate.c index e54ee572c..80d5bafdf 100644 --- a/src/libudev/libudev-enumerate.c +++ b/src/libudev/libudev-enumerate.c @@ -277,9 +277,6 @@ _public_ int udev_enumerate_add_match_tag(struct udev_enumerate *udev_enumerate, * Return the devices on the subtree of one given device. The parent * itself is included in the list. * - * A reference for the device is held until the udev_enumerate context - * is cleaned up. - * * Returns: 0 on success, otherwise a negative error value. */ _public_ int udev_enumerate_add_match_parent(struct udev_enumerate *udev_enumerate, struct udev_device *parent) { diff --git a/src/libudev/libudev-list.c b/src/libudev/libudev-list.c index 273732618..00fd58b9e 100644 --- a/src/libudev/libudev-list.c +++ b/src/libudev/libudev-list.c @@ -7,7 +7,7 @@ #include "alloc-util.h" #include "libudev-list-internal.h" -#include "util.h" +#include "memory-util.h" /** * SECTION:libudev-list diff --git a/src/libudev/libudev-util.c b/src/libudev/libudev-util.c index 7e21719fb..37660d031 100644 --- a/src/libudev/libudev-util.c +++ b/src/libudev/libudev-util.c @@ -175,7 +175,7 @@ size_t util_replace_chars(char *str, const char *white) { } /* accept valid utf8 */ - len = utf8_encoded_valid_unichar(&str[i]); + len = utf8_encoded_valid_unichar(str + i, (size_t) -1); if (len > 1) { i += len; continue; diff --git a/src/locale/keymap-util.c b/src/locale/keymap-util.c index 6b6b32a59..b8bd181c1 100644 --- a/src/locale/keymap-util.c +++ b/src/locale/keymap-util.c @@ -3,20 +3,23 @@ #include #include #include +#include +#include #include #include "bus-util.h" -#include "def.h" -#include "env-file.h" #include "env-file-label.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio-label.h" #include "fileio.h" +#include "kbd-util.h" #include "keymap-util.h" #include "locale-util.h" #include "macro.h" #include "mkdir.h" +#include "nulstr-util.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" @@ -29,10 +32,6 @@ static bool startswith_comma(const char *s, const char *prefix) { return IN_SET(*s, ',', '\0'); } -static const char* strnulldash(const char *s) { - return isempty(s) || streq(s, "-") ? NULL : s; -} - static const char* systemd_kbd_model_map(void) { const char* s; @@ -419,8 +418,7 @@ int x11_write_data(Context *c) { return 0; } - mkdir_p_label("/etc/X11/xorg.conf.d", 0755); - + (void) mkdir_p_label("/etc/X11/xorg.conf.d", 0755); r = fopen_temporary("/etc/X11/xorg.conf.d/00-keyboard.conf", &f, &temp_path); if (r < 0) return r; @@ -549,15 +547,15 @@ int vconsole_convert_to_x11(Context *c) { if (!streq(c->vc_keymap, a[0])) continue; - if (!streq_ptr(c->x11_layout, strnulldash(a[1])) || - !streq_ptr(c->x11_model, strnulldash(a[2])) || - !streq_ptr(c->x11_variant, strnulldash(a[3])) || - !streq_ptr(c->x11_options, strnulldash(a[4]))) { + if (!streq_ptr(c->x11_layout, empty_or_dash_to_null(a[1])) || + !streq_ptr(c->x11_model, empty_or_dash_to_null(a[2])) || + !streq_ptr(c->x11_variant, empty_or_dash_to_null(a[3])) || + !streq_ptr(c->x11_options, empty_or_dash_to_null(a[4]))) { - if (free_and_strdup(&c->x11_layout, strnulldash(a[1])) < 0 || - free_and_strdup(&c->x11_model, strnulldash(a[2])) < 0 || - free_and_strdup(&c->x11_variant, strnulldash(a[3])) < 0 || - free_and_strdup(&c->x11_options, strnulldash(a[4])) < 0) + if (free_and_strdup(&c->x11_layout, empty_or_dash_to_null(a[1])) < 0 || + free_and_strdup(&c->x11_model, empty_or_dash_to_null(a[2])) < 0 || + free_and_strdup(&c->x11_variant, empty_or_dash_to_null(a[3])) < 0 || + free_and_strdup(&c->x11_options, empty_or_dash_to_null(a[4])) < 0) return -ENOMEM; modified = true; diff --git a/src/locale/localectl.c b/src/locale/localectl.c index 69f566780..c8b195d9a 100644 --- a/src/locale/localectl.c +++ b/src/locale/localectl.c @@ -11,18 +11,18 @@ #include "bus-error.h" #include "bus-util.h" -#include "def.h" #include "fd-util.h" #include "fileio.h" +#include "kbd-util.h" #include "locale-util.h" #include "main-func.h" +#include "memory-util.h" #include "pager.h" #include "pretty-print.h" #include "proc-cmdline.h" #include "set.h" #include "spawn-polkit-agent.h" #include "strv.h" -#include "util.h" #include "verbs.h" #include "virt.h" diff --git a/src/locale/localed.c b/src/locale/localed.c index f851d35a0..8d0eec96a 100644 --- a/src/locale/localed.c +++ b/src/locale/localed.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #if HAVE_XKBCOMMON diff --git a/src/locale/test-keymap-util.c b/src/locale/test-keymap-util.c index 2f82891d6..f96f5b07e 100644 --- a/src/locale/test-keymap-util.c +++ b/src/locale/test-keymap-util.c @@ -4,6 +4,7 @@ #include "keymap-util.h" #include "log.h" #include "string-util.h" +#include "tests.h" static void test_find_language_fallback(void) { _cleanup_free_ char *ans = NULL, *ans2 = NULL; @@ -189,8 +190,7 @@ static void test_x11_convert_to_vconsole(void) { } int main(int argc, char **argv) { - log_set_max_level(LOG_DEBUG); - log_parse_environment(); + test_setup_logging(LOG_DEBUG); test_find_language_fallback(); test_find_converted_keymap(); diff --git a/src/login/70-uaccess.rules.m4 b/src/login/70-uaccess.rules.m4 index d55e5bf5c..4bb144a93 100644 --- a/src/login/70-uaccess.rules.m4 +++ 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(`GROUP_RENDER_UACCESS',`` +# DRI render nodes +SUBSYSTEM=="drm", KERNEL=="renderD*", TAG+="uaccess"'' +)m4_dnl m4_ifdef(`DEV_KVM_UACCESS',`` # KVM SUBSYSTEM=="misc", KERNEL=="kvm", TAG+="uaccess"'' diff --git a/src/login/71-seat.rules.in b/src/login/71-seat.rules.in index 1f3090039..b67966cdf 100644 --- a/src/login/71-seat.rules.in +++ b/src/login/71-seat.rules.in @@ -13,6 +13,13 @@ 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" + +# HyperV currently doesn't do DRM, hence we need to synthesize for HyperV's fb device instead +SUBSYSTEM=="graphics", KERNEL=="fb[0-9]", DRIVERS=="hyperv_fb", TAG+="master-of-seat" + +# Allow efifb / uvesafb to be a master if KMS is disabled +SUBSYSTEM=="graphics", KERNEL=="fb[0-9]", IMPORT{cmdline}="nomodeset", TAG+="master-of-seat" + SUBSYSTEM=="drm", KERNEL=="card[0-9]*", TAG+="seat", TAG+="master-of-seat" SUBSYSTEM=="usb", ATTR{bDeviceClass}=="09", TAG+="seat" @@ -24,6 +31,11 @@ SUBSYSTEM=="usb", ATTR{idVendor}=="2230", ATTR{idProduct}=="000[13]", ENV{ID_AUT # http://git.qemu.org/?p=qemu.git;a=blob;f=docs/multiseat.txt SUBSYSTEM=="pci", ATTR{vendor}=="0x1b36", ATTR{device}=="0x000a", TAG+="seat", ENV{ID_AUTOSEAT}="1" +# Video adapter of Parallels virtualization platform +# Seat should be synthesized for it. But there's no in-kernel driver for this +# device so matching by vid/pid. +SUBSYSTEM=="pci", ATTRS{vendor}=="0x1ab8", ATTRS{device}=="0x4005", TAG+="seat", TAG+="master-of-seat" + # Mimo 720, with integrated USB hub, displaylink graphics, and e2i # touchscreen. This device carries no proper VID/PID in the USB hub, # but it does carry good ID data in the graphics component, hence we diff --git a/src/login/inhibit.c b/src/login/inhibit.c index f574d429f..47fd9e874 100644 --- a/src/login/inhibit.c +++ b/src/login/inhibit.c @@ -286,8 +286,9 @@ static int run(int argc, char *argv[]) { else { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - _cleanup_close_ int fd = -1; + _cleanup_strv_free_ char **arguments = NULL; _cleanup_free_ char *w = NULL; + _cleanup_close_ int fd = -1; pid_t pid; /* Ignore SIGINT and allow the forked process to receive it */ @@ -303,12 +304,16 @@ static int run(int argc, char *argv[]) { if (fd < 0) return log_error_errno(fd, "Failed to inhibit: %s", bus_error_message(&error, fd)); + arguments = strv_copy(argv + optind); + if (!arguments) + return log_oom(); + r = safe_fork("(inhibit)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_CLOSE_ALL_FDS|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { /* Child */ - execvp(argv[optind], argv + optind); + execvp(arguments[0], arguments); log_open(); log_error_errno(errno, "Failed to execute %s: %m", argv[optind]); _exit(EXIT_FAILURE); diff --git a/src/login/loginctl.c b/src/login/loginctl.c index ab1b56201..342ac56d9 100644 --- a/src/login/loginctl.c +++ b/src/login/loginctl.c @@ -10,7 +10,7 @@ #include "alloc-util.h" #include "bus-error.h" -#include "bus-unit-util.h" +#include "bus-unit-procs.h" #include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" @@ -19,6 +19,7 @@ #include "logs-show.h" #include "macro.h" #include "main-func.h" +#include "memory-util.h" #include "pager.h" #include "parse-util.h" #include "pretty-print.h" @@ -33,7 +34,6 @@ #include "terminal-util.h" #include "unit-name.h" #include "user-util.h" -#include "util.h" #include "verbs.h" static char **arg_property = NULL; @@ -755,7 +755,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); if (all || !isempty(s)) - bus_print_property_value(name, expected_value, value, "%s", s); + bus_print_property_value(name, expected_value, value, s); return 1; @@ -771,7 +771,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m "Invalid user ID: " UID_FMT, uid); - bus_print_property_value(name, expected_value, value, UID_FMT, uid); + bus_print_property_valuef(name, expected_value, value, UID_FMT, uid); return 1; } break; diff --git a/src/login/logind-core.c b/src/login/logind-core.c index 60831bb1c..2467da18e 100644 --- a/src/login/logind-core.c +++ b/src/login/logind-core.c @@ -18,12 +18,14 @@ #include "conf-parser.h" #include "device-util.h" #include "fd-util.h" +#include "limits-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) { @@ -238,14 +240,12 @@ int manager_add_button(Manager *m, const char *name, Button **_button) { } int manager_process_seat_device(Manager *m, sd_device *d) { - const char *action; Device *device; int r; assert(m); - if (sd_device_get_property_value(d, "ACTION", &action) >= 0 && - streq(action, "remove")) { + if (device_for_action(d, DEVICE_ACTION_REMOVE)) { const char *syspath; r = sd_device_get_syspath(d, &syspath); @@ -305,7 +305,7 @@ int manager_process_seat_device(Manager *m, sd_device *d) { } int manager_process_button_device(Manager *m, sd_device *d) { - const char *action, *sysname; + const char *sysname; Button *b; int r; @@ -315,8 +315,7 @@ int manager_process_button_device(Manager *m, sd_device *d) { if (r < 0) return r; - if (sd_device_get_property_value(d, "ACTION", &action) >= 0 && - streq(action, "remove")) { + if (device_for_action(d, DEVICE_ACTION_REMOVE)) { b = hashmap_get(m->buttons, sysname); if (!b) diff --git a/src/login/logind-dbus.c b/src/login/logind-dbus.c index 8ab498fdc..2cebcce12 100644 --- a/src/login/logind-dbus.c +++ b/src/login/logind-dbus.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "sd-device.h" @@ -10,6 +11,7 @@ #include "alloc-util.h" #include "audit-util.h" +#include "bootspec.h" #include "bus-common-errors.h" #include "bus-error.h" #include "bus-unit-util.h" @@ -18,6 +20,7 @@ #include "device-util.h" #include "dirent-util.h" #include "efivars.h" +#include "env-util.h" #include "escape.h" #include "fd-util.h" #include "fileio-label.h" @@ -27,17 +30,21 @@ #include "logind.h" #include "missing_capability.h" #include "mkdir.h" +#include "parse-util.h" #include "path-util.h" #include "process-util.h" +#include "reboot-util.h" #include "selinux-util.h" #include "sleep-config.h" #include "special.h" +#include "stdio-util.h" #include "strv.h" #include "terminal-util.h" #include "tmpfile-util.h" #include "unit-name.h" #include "user-util.h" #include "utmp-wtmp.h" +#include "virt.h" static int get_sender_session(Manager *m, sd_bus_message *message, sd_bus_error *error, Session **ret) { @@ -115,7 +122,8 @@ static int get_sender_user(Manager *m, sd_bus_message *message, sd_bus_error *er return 0; err_no_user: - return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, "Caller does not belong to any logged in user or lingering user"); + return sd_bus_error_setf(error, BUS_ERROR_NO_USER_FOR_PID, + "Caller does not belong to any logged in user or lingering user"); } int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, sd_bus_error *error, User **ret) { @@ -130,7 +138,8 @@ int manager_get_user_from_creds(Manager *m, sd_bus_message *message, uid_t uid, user = hashmap_get(m->users, UID_TO_PTR(uid)); if (!user) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, "User ID "UID_FMT" is not logged in or lingering", uid); + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_USER, + "User ID "UID_FMT" is not logged in or lingering", uid); *ret = user; return 0; @@ -164,6 +173,32 @@ int manager_get_seat_from_creds(Manager *m, sd_bus_message *message, const char return 0; } +static int return_test_polkit( + sd_bus_message *message, + int capability, + const char *action, + const char **details, + uid_t good_user, + sd_bus_error *e) { + + const char *result; + bool challenge; + int r; + + r = bus_test_polkit(message, capability, action, details, good_user, &challenge, e); + if (r < 0) + return r; + + if (r > 0) + result = "yes"; + else if (challenge) + result = "challenge"; + else + result = "no"; + + return sd_bus_reply_method_return(message, "s", result); +} + static int property_get_idle_hint( sd_bus *bus, const char *path, @@ -338,7 +373,8 @@ static int method_get_session_by_pid(sd_bus_message *message, void *userdata, sd return r; if (!session) - return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, "PID "PID_FMT" does not belong to any known session", pid); + return sd_bus_error_setf(error, BUS_ERROR_NO_SESSION_FOR_PID, + "PID "PID_FMT" does not belong to any known session", pid); } p = session_bus_path(session); @@ -616,7 +652,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus assert_cc(sizeof(pid_t) == sizeof(uint32_t)); assert_cc(sizeof(uid_t) == sizeof(uint32_t)); - r = sd_bus_message_read(message, "uusssssussbss", &uid, &leader, &service, &type, &class, &desktop, &cseat, &vtnr, &tty, &display, &remote, &remote_user, &remote_host); + r = sd_bus_message_read(message, "uusssssussbss", + &uid, &leader, &service, &type, &class, &desktop, &cseat, + &vtnr, &tty, &display, &remote, &remote_user, &remote_host); if (r < 0) return r; @@ -630,7 +668,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus else { t = session_type_from_string(type); if (t < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session type %s", type); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid session type %s", type); } if (isempty(class)) @@ -638,14 +677,16 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus else { c = session_class_from_string(class); if (c < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid session class %s", class); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid session class %s", class); } if (isempty(desktop)) desktop = NULL; else { if (!string_is_safe(desktop)) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid desktop string %s", desktop); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid desktop string %s", desktop); } if (isempty(cseat)) @@ -653,7 +694,8 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus else { seat = hashmap_get(m->seats, cseat); if (!seat) - return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, "No seat '%s' known", cseat); + return sd_bus_error_setf(error, BUS_ERROR_NO_SUCH_SEAT, + "No seat '%s' known", cseat); } if (tty_is_vc(tty)) { @@ -662,35 +704,42 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus if (!seat) seat = m->seat0; else if (seat != m->seat0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TTY %s is virtual console but seat %s is not seat0", tty, seat->id); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "TTY %s is virtual console but seat %s is not seat0", tty, seat->id); v = vtnr_from_tty(tty); if (v <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Cannot determine VT number from virtual console TTY %s", tty); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Cannot determine VT number from virtual console TTY %s", tty); if (vtnr == 0) vtnr = (uint32_t) v; else if (vtnr != (uint32_t) v) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Specified TTY and VT number do not match"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Specified TTY and VT number do not match"); } else if (tty_is_console(tty)) { if (!seat) seat = m->seat0; else if (seat != m->seat0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but seat is not seat0"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Console TTY specified but seat is not seat0"); if (vtnr != 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Console TTY specified but VT number is not 0"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Console TTY specified but VT number is not 0"); } if (seat) { if (seat_has_vts(seat)) { if (vtnr <= 0 || vtnr > 63) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "VT number out of range"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "VT number out of range"); } else { if (vtnr != 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Seat has no VTs but VT number not 0"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Seat has no VTs but VT number not 0"); } } @@ -722,13 +771,14 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus return r; } - /* Check if we are already in a logind session. Or if we are in user@.service which is a special PAM session - * that avoids creating a logind session. */ + /* Check if we are already in a logind session. Or if we are in user@.service + * which is a special PAM session that avoids creating a logind session. */ r = manager_get_user_by_pid(m, leader, NULL); if (r < 0) return r; if (r > 0) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already running in a session or user slice"); + return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, + "Already running in a session or user slice"); /* * Old gdm and lightdm start the user-session on the same VT as @@ -748,7 +798,9 @@ static int method_create_session(sd_bus_message *message, void *userdata, sd_bus return sd_bus_error_setf(error, BUS_ERROR_SESSION_BUSY, "Already occupied by a session"); if (hashmap_size(m->sessions) >= m->sessions_max) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", m->sessions_max); + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, + "Maximum number of sessions (%" PRIu64 ") reached, refusing further sessions.", + m->sessions_max); (void) audit_session_from_pid(leader, &audit_id); if (audit_session_is_valid(audit_id)) { @@ -790,7 +842,9 @@ 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); + r = session_set_leader(session, leader); + if (r < 0) + goto fail; session->type = t; session->class = c; @@ -952,7 +1006,8 @@ static int method_activate_session_on_seat(sd_bus_message *message, void *userda return r; if (session->seat != seat) - return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, "Session %s not on seat %s", session_name, seat_name); + return sd_bus_error_setf(error, BUS_ERROR_SESSION_NOT_ON_SEAT, + "Session %s not on seat %s", session_name, seat_name); r = session_activate(session); if (r < 0) @@ -1163,8 +1218,7 @@ static int method_set_user_linger(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 */ - mkdir_p_label("/var/lib/systemd", 0755); - + (void) mkdir_p_label("/var/lib/systemd", 0755); r = mkdir_safe_label("/var/lib/systemd/linger", 0755, 0, 0, MKDIR_WARN_MODE); if (r < 0) return r; @@ -1761,7 +1815,8 @@ static int method_do_shutdown_or_sleep( /* Don't allow multiple jobs being executed at the same time */ if (m->action_what > 0) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "There's already a shutdown or sleep operation in progress"); + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, + "There's already a shutdown or sleep operation in progress"); if (sleep_verb) { r = can_sleep(sleep_verb); @@ -2376,6 +2431,97 @@ static int method_can_suspend_then_hibernate(sd_bus_message *message, void *user error); } +static int property_get_reboot_parameter( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + _cleanup_free_ char *parameter = NULL; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = read_reboot_parameter(¶meter); + if (r < 0) + return r; + + return sd_bus_message_append(reply, "s", parameter); +} + +static int method_set_reboot_parameter( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + const char *arg; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &arg); + if (r < 0) + return r; + + r = detect_container(); + if (r < 0) + return r; + if (r > 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, + "Reboot parameter not supported in containers, refusing."); + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-parameter", + NULL, + false, + UID_INVALID, + &m->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 = update_reboot_parameter_and_warn(arg, false); + if (r < 0) + return r; + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_parameter( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = detect_container(); + if (r < 0) + return r; + if (r > 0) /* Inside containers, specifying a reboot parameter, doesn't make much sense */ + return sd_bus_reply_method_return(message, "s", "na"); + + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-parameter", + NULL, + UID_INVALID, + error); +} + static int property_get_reboot_to_firmware_setup( sd_bus *bus, const char *path, @@ -2390,9 +2536,24 @@ static int property_get_reboot_to_firmware_setup( assert(reply); assert(userdata); - r = efi_get_reboot_to_firmware(); - if (r < 0 && r != -EOPNOTSUPP) - log_warning_errno(r, "Failed to determine reboot-to-firmware state: %m"); + r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP"); + if (r == -ENXIO) { + /* EFI case: let's see what is currently configured in the EFI variables */ + r = efi_get_reboot_to_firmware(); + if (r < 0 && r != -EOPNOTSUPP) + log_warning_errno(r, "Failed to determine reboot-to-firmware-setup state: %m"); + } else if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); + else if (r > 0) { + /* Non-EFI case: let's see whether /run/systemd/reboot-to-firmware-setup exists. */ + if (access("/run/systemd/reboot-to-firmware-setup", F_OK) < 0) { + if (errno != ENOENT) + log_warning_errno(errno, "Failed to check whether /run/systemd/reboot-to-firmware-setup exists: %m"); + + r = false; + } else + r = true; + } return sd_bus_message_append(reply, "b", r > 0); } @@ -2402,8 +2563,9 @@ static int method_set_reboot_to_firmware_setup( void *userdata, sd_bus_error *error) { - int b, r; Manager *m = userdata; + bool use_efi; + int b, r; assert(message); assert(m); @@ -2412,6 +2574,29 @@ static int method_set_reboot_to_firmware_setup( if (r < 0) return r; + r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP"); + if (r == -ENXIO) { + /* EFI case: let's see what the firmware supports */ + + r = efi_reboot_to_firmware_supported(); + if (r == -EOPNOTSUPP) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware."); + if (r < 0) + return r; + + use_efi = true; + + } else if (r <= 0) { + /* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to off */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); + + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Firmware does not support boot into firmware."); + } else + /* non-EFI case: $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP is set to on */ + use_efi = false; + r = bus_verify_polkit_async(message, CAP_SYS_ADMIN, "org.freedesktop.login1.set-reboot-to-firmware-setup", @@ -2425,9 +2610,20 @@ static int method_set_reboot_to_firmware_setup( if (r == 0) return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */ - r = efi_set_reboot_to_firmware(b); - if (r < 0) - return r; + if (use_efi) { + r = efi_set_reboot_to_firmware(b); + if (r < 0) + return r; + } else { + if (b) { + r = touch("/run/systemd/reboot-to-firmware-setup"); + if (r < 0) + return r; + } else { + if (unlink("/run/systemd/reboot-to-firmware-setup") < 0 && errno != ENOENT) + return -errno; + } + } return sd_bus_reply_method_return(message, NULL); } @@ -2437,40 +2633,458 @@ static int method_can_reboot_to_firmware_setup( void *userdata, sd_bus_error *error) { - int r; - bool challenge; - const char *result; Manager *m = userdata; + int r; assert(message); assert(m); - r = efi_reboot_to_firmware_supported(); - if (r < 0) { - if (r != -EOPNOTSUPP) - log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m"); + r = getenv_bool("SYSTEMD_REBOOT_TO_FIRMWARE_SETUP"); + if (r == -ENXIO) { + /* EFI case: let's see what the firmware supports */ + + r = efi_reboot_to_firmware_supported(); + if (r < 0) { + if (r != -EOPNOTSUPP) + log_warning_errno(r, "Failed to determine whether reboot to firmware is supported: %m"); + + return sd_bus_reply_method_return(message, "s", "na"); + } + + } else if (r <= 0) { + /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_FIRMWARE_SETUP: %m"); return sd_bus_reply_method_return(message, "s", "na"); } - r = bus_test_polkit(message, - CAP_SYS_ADMIN, - "org.freedesktop.login1.set-reboot-to-firmware-setup", - NULL, - UID_INVALID, - &challenge, - error); + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-firmware-setup", + NULL, + UID_INVALID, + error); +} + +static int property_get_reboot_to_boot_loader_menu( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t x = UINT64_MAX; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); + if (r == -ENXIO) { + _cleanup_free_ char *v = NULL; + + /* EFI case: returns the current value of LoaderConfigTimeoutOneShot. Three cases are distuingished: + * + * 1. Variable not set, boot into boot loader menu is not enabled (we return UINT64_MAX to the user) + * 2. Variable set to "0", boot into boot loader menu is enabled with no timeout (we return 0 to the user) + * 3. Variable set to numeric value formatted in ASCII, boot into boot loader menu with the specified timeout in seconds + */ + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read LoaderConfigTimeoutOneShot variable: %m"); + } else { + uint64_t sec; + + r = safe_atou64(v, &sec); + if (r < 0) + log_warning_errno(r, "Failed to parse LoaderConfigTimeoutOneShot value '%s': %m", v); + else if (sec > (USEC_INFINITY / USEC_PER_SEC)) + log_warning("LoaderConfigTimeoutOneShot too large, ignoring: %m"); + else + x = sec * USEC_PER_SEC; /* return in µs */ + } + + } else if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); + else if (r > 0) { + _cleanup_free_ char *v = NULL; + + /* Non-EFI case, let's process /run/systemd/reboot-to-boot-loader-menu. */ + + r = read_one_line_file("/run/systemd/reboot-to-boot-loader-menu", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-menu: %m"); + } else { + r = safe_atou64(v, &x); + if (r < 0) + log_warning_errno(r, "Failed to parse /run/systemd/reboot-to-boot-loader-menu: %m"); + } + } + + return sd_bus_message_append(reply, "t", x); +} + +static int method_set_reboot_to_boot_loader_menu( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + bool use_efi; + uint64_t x; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "t", &x); if (r < 0) return r; - if (r > 0) - result = "yes"; - else if (challenge) - result = "challenge"; - else - result = "no"; + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); + if (r == -ENXIO) { + uint64_t features; - return sd_bus_reply_method_return(message, "s", result); + /* EFI case: let's see if booting into boot loader menu is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu."); + + use_efi = true; + + } else if (r <= 0) { + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to off */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); + + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader does not support boot into boot loader menu."); + } else + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU is set to on */ + use_efi = false; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-menu", + NULL, + false, + UID_INVALID, + &m->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 */ + + if (use_efi) { + if (x == UINT64_MAX) + r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", NULL, 0); + else { + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + xsprintf(buf, "%" PRIu64, DIV_ROUND_UP(x, USEC_PER_SEC)); /* second granularity */ + + r = efi_set_variable_string(EFI_VENDOR_LOADER, "LoaderConfigTimeoutOneShot", buf); + } + if (r < 0) + return r; + } else { + if (x == UINT64_MAX) { + if (unlink("/run/systemd/reboot-to-loader-menu") < 0 && errno != ENOENT) + return -errno; + } else { + char buf[DECIMAL_STR_MAX(uint64_t) + 1]; + + xsprintf(buf, "%" PRIu64, x); /* µs granularity */ + + r = write_string_file_atomic_label("/run/systemd/reboot-to-loader-menu", buf); + if (r < 0) + return r; + } + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_boot_loader_menu( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU"); + if (r == -ENXIO) { + uint64_t features = 0; + + /* EFI case, let's see if booting into boot loader menu is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot to boot loader menu is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_CONFIG_TIMEOUT_ONE_SHOT)) + return sd_bus_reply_method_return(message, "s", "na"); + + } else if (r <= 0) { + /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_MENU: %m"); + + return sd_bus_reply_method_return(message, "s", "na"); + } + + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-menu", + NULL, + UID_INVALID, + error); +} + +static int property_get_reboot_to_boot_loader_entry( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_free_ char *v = NULL; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); + if (r == -ENXIO) { + /* EFI case: let's read the LoaderEntryOneShot variable */ + + r = efi_get_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read LoaderEntryOneShot variable: %m"); + } else if (!efi_loader_entry_name_valid(v)) { + log_warning("LoaderEntryOneShot contains invalid entry name '%s', ignoring.", v); + v = mfree(v); + } + } else if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); + else if (r > 0) { + + /* Non-EFI case, let's process /run/systemd/reboot-to-boot-loader-entry. */ + + r = read_one_line_file("/run/systemd/reboot-to-boot-loader-entry", &v); + if (r < 0) { + if (r != -ENOENT) + log_warning_errno(r, "Failed to read /run/systemd/reboot-to-boot-loader-entry: %m"); + } else if (!efi_loader_entry_name_valid(v)) { + log_warning("/run/systemd/reboot-to-boot-loader-entry is not valid, ignoring."); + v = mfree(v); + } + } + + return sd_bus_message_append(reply, "s", v); +} + +static int boot_loader_entry_exists(const char *id) { + _cleanup_(boot_config_free) BootConfig config = {}; + int r; + + assert(id); + + r = boot_entries_load_config_auto(NULL, NULL, &config); + if (r < 0 && r != -ENOKEY) /* don't complain if no GPT is found, hence skip ENOKEY */ + return r; + + (void) boot_entries_augment_from_loader(&config, true); + + return boot_config_has_entry(&config, id); +} + +static int method_set_reboot_to_boot_loader_entry( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + bool use_efi; + const char *v; + int r; + + assert(message); + assert(m); + + r = sd_bus_message_read(message, "s", &v); + if (r < 0) + return r; + + if (isempty(v)) + v = NULL; + else if (efi_loader_entry_name_valid(v)) { + r = boot_loader_entry_exists(v); + if (r < 0) + return r; + if (r == 0) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Boot loader entry '%s' is not known.", v); + } else + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Boot loader entry name '%s' is not valid, refusing.", v); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); + if (r == -ENXIO) { + uint64_t features; + + /* EFI case: let's see if booting into boot loader entry is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot into boot loader entry is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT)) + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry."); + + use_efi = true; + + } else if (r <= 0) { + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to off */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); + + return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Loader does not support boot into boot loader entry."); + } else + /* non-EFI case: $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY is set to on */ + use_efi = false; + + r = bus_verify_polkit_async(message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-entry", + NULL, + false, + UID_INVALID, + &m->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 */ + + if (use_efi) { + if (isempty(v)) + /* Delete item */ + r = efi_set_variable(EFI_VENDOR_LOADER, "LoaderEntryOneShot", NULL, 0); + else + r = efi_set_variable_string(EFI_VENDOR_LOADER, "LoaderEntryOneShot", v); + if (r < 0) + return r; + } else { + if (isempty(v)) { + if (unlink("/run/systemd/reboot-to-boot-loader-entry") < 0 && errno != ENOENT) + return -errno; + } else { + r = write_string_file_atomic_label("/run/systemd/reboot-boot-to-loader-entry", v); + if (r < 0) + return r; + } + } + + return sd_bus_reply_method_return(message, NULL); +} + +static int method_can_reboot_to_boot_loader_entry( + sd_bus_message *message, + void *userdata, + sd_bus_error *error) { + + Manager *m = userdata; + int r; + + assert(message); + assert(m); + + r = getenv_bool("SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY"); + if (r == -ENXIO) { + uint64_t features = 0; + + /* EFI case, let's see if booting into boot loader entry is supported. */ + + r = efi_loader_get_features(&features); + if (r < 0) + log_warning_errno(r, "Failed to determine whether reboot to boot loader entry is supported: %m"); + if (r < 0 || !FLAGS_SET(features, EFI_LOADER_FEATURE_ENTRY_ONESHOT)) + return sd_bus_reply_method_return(message, "s", "na"); + + } else if (r <= 0) { + /* Non-EFI case: let's trust $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY */ + + if (r < 0) + log_warning_errno(r, "Failed to parse $SYSTEMD_REBOOT_TO_BOOT_LOADER_ENTRY: %m"); + + return sd_bus_reply_method_return(message, "s", "na"); + } + + return return_test_polkit( + message, + CAP_SYS_ADMIN, + "org.freedesktop.login1.set-reboot-to-boot-loader-entry", + NULL, + UID_INVALID, + error); +} + +static int property_get_boot_loader_entries( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + _cleanup_(boot_config_free) BootConfig config = {}; + size_t i; + int r; + + assert(bus); + assert(reply); + assert(userdata); + + r = boot_entries_load_config_auto(NULL, NULL, &config); + if (r < 0 && r != -ENOKEY) /* don't complain if there's no GPT found */ + return r; + + (void) boot_entries_augment_from_loader(&config, true); + + r = sd_bus_message_open_container(reply, 'a', "s"); + if (r < 0) + return r; + + for (i = 0; i < config.n_entries; i++) { + BootEntry *e = config.entries + i; + + r = sd_bus_message_append(reply, "s", e->id); + if (r < 0) + return r; + } + + return sd_bus_message_close_container(reply); } static int method_set_wall_message( @@ -2534,22 +3148,26 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error w = inhibit_what_from_string(what); if (w <= 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid what specification %s", what); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid what specification %s", what); mm = inhibit_mode_from_string(mode); if (mm < 0) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Invalid mode specification %s", mode); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Invalid mode specification %s", mode); /* Delay is only supported for shutdown/sleep */ if (mm == INHIBIT_DELAY && (w & ~(INHIBIT_SHUTDOWN|INHIBIT_SLEEP))) - return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Delay inhibitors only supported for shutdown and sleep"); + return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, + "Delay inhibitors only supported for shutdown and sleep"); /* Don't allow taking delay locks while we are already * executing the operation. We shouldn't create the impression * that the lock was successful if the machine is about to go * down/suspend any moment. */ if (m->action_what & w) - return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, "The operation inhibition has been requested for is already running"); + return sd_bus_error_setf(error, BUS_ERROR_OPERATION_IN_PROGRESS, + "The operation inhibition has been requested for is already running"); r = bus_verify_polkit_async( message, @@ -2584,7 +3202,9 @@ static int method_inhibit(sd_bus_message *message, void *userdata, sd_bus_error return r; if (hashmap_size(m->inhibitors) >= m->inhibitors_max) - return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", m->inhibitors_max); + return sd_bus_error_setf(error, SD_BUS_ERROR_LIMITS_EXCEEDED, + "Maximum number of inhibitors (%" PRIu64 ") reached, refusing further inhibitors.", + m->inhibitors_max); do { id = mfree(id); @@ -2637,7 +3257,11 @@ 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("RebootParameter", "s", property_get_reboot_parameter, 0, 0), SD_BUS_PROPERTY("RebootToFirmwareSetup", "b", property_get_reboot_to_firmware_setup, 0, 0), + SD_BUS_PROPERTY("RebootToBootLoaderMenu", "t", property_get_reboot_to_boot_loader_menu, 0, 0), + SD_BUS_PROPERTY("RebootToBootLoaderEntry", "s", property_get_reboot_to_boot_loader_entry, 0, 0), + SD_BUS_PROPERTY("BootLoaderEntries", "as", property_get_boot_loader_entries, 0, SD_BUS_VTABLE_PROPERTY_CONST), 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), @@ -2710,8 +3334,14 @@ const sd_bus_vtable manager_vtable[] = { SD_BUS_METHOD("ScheduleShutdown", "st", NULL, method_schedule_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CancelScheduledShutdown", NULL, "b", method_cancel_scheduled_shutdown, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Inhibit", "ssss", "h", method_inhibit, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootParameter", NULL, "s", method_can_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootParameter", "s", NULL, method_set_reboot_parameter, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("CanRebootToFirmwareSetup", NULL, "s", method_can_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetRebootToFirmwareSetup", "b", NULL, method_set_reboot_to_firmware_setup, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToBootLoaderMenu", NULL, "s", method_can_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToBootLoaderMenu", "t", NULL, method_set_reboot_to_boot_loader_menu, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("CanRebootToBootLoaderEntry", NULL, "s", method_can_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("SetRebootToBootLoaderEntry", "s", NULL, method_set_reboot_to_boot_loader_entry, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("SetWallMessage", "sb", NULL, method_set_wall_message, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_SIGNAL("SessionNew", "so", 0), @@ -2736,7 +3366,8 @@ static int session_jobs_reply(Session *s, const char *unit, const char *result) 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); + 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); } @@ -3156,7 +3787,7 @@ int manager_unit_is_active(Manager *manager, const char *unit, sd_bus_error *ret &reply, "s"); if (r < 0) { - /* systemd might have droppped off momentarily, let's + /* systemd might have dropped off momentarily, let's * not make this an error */ if (sd_bus_error_has_name(&error, SD_BUS_ERROR_NO_REPLY) || sd_bus_error_has_name(&error, SD_BUS_ERROR_DISCONNECTED)) diff --git a/src/login/logind-inhibit.c b/src/login/logind-inhibit.c index 415c26b14..d427dcaa0 100644 --- a/src/login/logind-inhibit.c +++ b/src/login/logind-inhibit.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include "alloc-util.h" @@ -57,7 +59,7 @@ void inhibitor_free(Inhibitor *i) { free(i->why); if (i->state_file) { - unlink(i->state_file); + (void) unlink(i->state_file); free(i->state_file); } @@ -171,7 +173,7 @@ int inhibitor_stop(Inhibitor *i) { inhibit_mode_to_string(i->mode)); if (i->state_file) - unlink(i->state_file); + (void) unlink(i->state_file); i->started = false; @@ -318,7 +320,7 @@ void inhibitor_remove_fifo(Inhibitor *i) { i->fifo_fd = safe_close(i->fifo_fd); if (i->fifo_path) { - unlink(i->fifo_path); + (void) unlink(i->fifo_path); i->fifo_path = mfree(i->fifo_path); } } diff --git a/src/login/logind-seat.c b/src/login/logind-seat.c index a6d88f8e7..b4904c37d 100644 --- a/src/login/logind-seat.c +++ b/src/login/logind-seat.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "sd-messages.h" diff --git a/src/login/logind-session-device.c b/src/login/logind-session-device.c index f358524eb..7c79a4afd 100644 --- a/src/login/logind-session-device.c +++ b/src/login/logind-session-device.c @@ -387,21 +387,11 @@ void session_device_free(SessionDevice *sd) { assert(sd); /* Make sure to remove the pushed fd. */ - if (sd->pushed_fd) { - _cleanup_free_ char *m = NULL; - const char *id; - int r; - - /* Session ID does not contain separators. */ - id = sd->session->id; - assert(*(id + strcspn(id, "-\n")) == '\0'); - - r = asprintf(&m, "FDSTOREREMOVE=1\n" - "FDNAME=session-%s-device-%u-%u\n", - id, major(sd->dev), minor(sd->dev)); - if (r >= 0) - (void) sd_notify(false, m); - } + if (sd->pushed_fd) + (void) sd_notifyf(false, + "FDSTOREREMOVE=1\n" + "FDNAME=session-%s-device-%u-%u", + sd->session->id, major(sd->dev), minor(sd->dev)); session_device_stop(sd); session_device_notify(sd, SESSION_DEVICE_RELEASE); diff --git a/src/login/logind-session.c b/src/login/logind-session.c index 90a910856..3d3bc8ab1 100644 --- a/src/login/logind-session.c +++ b/src/login/logind-session.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include "sd-messages.h" @@ -643,20 +644,26 @@ static int session_start_scope(Session *s, sd_bus_message *properties, sd_bus_er s->leader, s->user->slice, description, - 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 */ + /* These two have StopWhenUnneeded= set, hence add a dep towards them */ + STRV_MAKE(s->user->runtime_dir_service, + s->user->service), + /* And order us after some more */ + STRV_MAKE("systemd-logind.service", + "systemd-user-sessions.service", + s->user->runtime_dir_service, + s->user->service), s->user->home, properties, 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)); + return log_error_errno(r, "Failed to start session scope %s: %s", + scope, bus_error_message(error, r)); s->scope = TAKE_PTR(scope); } - if (s->scope) - (void) hashmap_put(s->manager->session_units, s->scope, s); + (void) hashmap_put(s->manager->session_units, s->scope, s); return 0; } diff --git a/src/login/logind-user.c b/src/login/logind-user.c index ae27bfb66..045b6f0e1 100644 --- a/src/login/logind-user.c +++ b/src/login/logind-user.c @@ -19,6 +19,7 @@ #include "fs-util.h" #include "hashmap.h" #include "label.h" +#include "limits-util.h" #include "logind-user.h" #include "mkdir.h" #include "parse-util.h" @@ -68,6 +69,8 @@ int user_new(User **ret, if (!u->home) return -ENOMEM; + path_simplify(u->home, true); + if (asprintf(&u->state_file, "/run/systemd/users/"UID_FMT, uid) < 0) return -ENOMEM; @@ -357,7 +360,8 @@ static void user_start_service(User *u) { r = manager_start_unit(u->manager, u->service, &error, &u->service_job); if (r < 0) - log_warning_errno(r, "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); + log_full_errno(sd_bus_error_has_name(&error, BUS_ERROR_UNIT_MASKED) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to start user service '%s', ignoring: %s", u->service, bus_error_message(&error, r)); } int user_start(User *u) { diff --git a/src/login/logind.c b/src/login/logind.c index 95ec0a57c..4c0e8ce6b 100644 --- a/src/login/logind.c +++ b/src/login/logind.c @@ -26,6 +26,7 @@ #include "signal-util.h" #include "strv.h" #include "terminal-util.h" +#include "udev-util.h" static Manager* manager_unref(Manager *m); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_unref); @@ -377,73 +378,75 @@ static int parse_fdname(const char *fdname, char **session_id, dev_t *dev) { return 0; } +static int deliver_fd(Manager *m, const char *fdname, int fd) { + _cleanup_free_ char *id = NULL; + SessionDevice *sd; + struct stat st; + Session *s; + dev_t dev; + int r; + + assert(m); + assert(fd >= 0); + + r = parse_fdname(fdname, &id, &dev); + if (r < 0) + return log_debug_errno(r, "Failed to parse fd name %s: %m", fdname); + + s = hashmap_get(m->sessions, id); + if (!s) + /* If the session doesn't exist anymore, the associated session device attached to this fd + * doesn't either. Let's simply close this fd. */ + return log_debug_errno(SYNTHETIC_ERRNO(ENXIO), "Failed to attach fd for unknown session: %s", id); + + if (fstat(fd, &st) < 0) + /* The device is allowed to go away at a random point, in which case fstat() failing is + * expected. */ + return log_debug_errno(errno, "Failed to stat device fd for session %s: %m", id); + + if (!S_ISCHR(st.st_mode) || st.st_rdev != dev) + return log_debug_errno(SYNTHETIC_ERRNO(ENODEV), "Device fd doesn't point to the expected character device node"); + + sd = hashmap_get(s->devices, &dev); + if (!sd) + /* Weird, we got an fd for a session device which wasn't recorded in the session state + * file... */ + return log_warning_errno(SYNTHETIC_ERRNO(ENODEV), "Got fd for missing session device [%u:%u] in session %s", + major(dev), minor(dev), s->id); + + log_debug("Attaching fd to session device [%u:%u] for session %s", + major(dev), minor(dev), s->id); + + session_device_attach_fd(sd, fd, s->was_active); + return 0; +} + static int manager_attach_fds(Manager *m) { _cleanup_strv_free_ char **fdnames = NULL; - int n, i, fd; + int n; - /* Upon restart, PID1 will send us back all fds of session devices - * that we previously opened. Each file descriptor is associated - * with a given session. The session ids are passed through FDNAMES. */ + /* Upon restart, PID1 will send us back all fds of session devices that we previously opened. Each + * file descriptor is associated with a given session. The session ids are passed through FDNAMES. */ n = sd_listen_fds_with_names(true, &fdnames); - if (n <= 0) - return n; + if (n < 0) + return log_warning_errno(n, "Failed to acquire passed fd list: %m"); + if (n == 0) + return 0; - for (i = 0; i < n; i++) { - _cleanup_free_ char *id = NULL; - dev_t dev; - struct stat st; - SessionDevice *sd; - Session *s; - int r; + for (int i = 0; i < n; i++) { + int fd = SD_LISTEN_FDS_START + i; - fd = SD_LISTEN_FDS_START + i; - - r = parse_fdname(fdnames[i], &id, &dev); - if (r < 0) { - log_debug_errno(r, "Failed to parse fd name %s: %m", fdnames[i]); - close_nointr(fd); + if (deliver_fd(m, fdnames[i], fd) >= 0) continue; - } - s = hashmap_get(m->sessions, id); - if (!s) { - /* If the session doesn't exist anymore, the associated session - * device attached to this fd doesn't either. Let's simply close - * this fd. */ - log_debug("Failed to attach fd for unknown session: %s", id); - close_nointr(fd); - continue; - } + /* Hmm, we couldn't deliver the fd to any session device object? If so, let's close the fd */ + safe_close(fd); - if (fstat(fd, &st) < 0) { - /* The device is allowed to go away at a random point, in which - * case fstat failing is expected. */ - log_debug_errno(errno, "Failed to stat device fd for session %s: %m", id); - close_nointr(fd); - continue; - } - - if (!S_ISCHR(st.st_mode) || st.st_rdev != dev) { - log_debug("Device fd doesn't point to the expected character device node"); - close_nointr(fd); - continue; - } - - sd = hashmap_get(s->devices, &dev); - if (!sd) { - /* Weird, we got an fd for a session device which wasn't - * recorded in the session state file... */ - log_warning("Got fd for missing session device [%u:%u] in session %s", - major(dev), minor(dev), s->id); - close_nointr(fd); - continue; - } - - log_debug("Attaching fd to session device [%u:%u] for session %s", - major(dev), minor(dev), s->id); - - session_device_attach_fd(sd, fd, s->was_active); + /* Remove from fdstore as well */ + (void) sd_notifyf(false, + "FDSTOREREMOVE=1\n" + "FDNAME=%s", fdnames[i]); } return 0; @@ -491,11 +494,9 @@ static int manager_enumerate_sessions(Manager *m) { r = k; } - /* We might be restarted and PID1 could have sent us back the - * session device fds we previously saved. */ - k = manager_attach_fds(m); - if (k < 0) - log_warning_errno(k, "Failed to reattach session device fds: %m"); + /* We might be restarted and PID1 could have sent us back the session device fds we previously + * saved. */ + (void) manager_attach_fds(m); return r; } @@ -559,7 +560,7 @@ static int manager_dispatch_device_udev(sd_device_monitor *monitor, sd_device *d static int manager_dispatch_vcsa_udev(sd_device_monitor *monitor, sd_device *device, void *userdata) { Manager *m = userdata; - const char *name, *action; + const char *name; assert(m); assert(device); @@ -569,8 +570,7 @@ static int manager_dispatch_vcsa_udev(sd_device_monitor *monitor, sd_device *dev if (sd_device_get_sysname(device, &name) >= 0 && startswith(name, "vcsa") && - sd_device_get_property_value(device, "ACTION", &action) >= 0 && - streq(action, "remove")) + device_for_action(device, DEVICE_ACTION_REMOVE)) seat_preallocate_vts(m->seat0); return 0; diff --git a/src/login/logind.h b/src/login/logind.h index 7b774f49e..7b6f73c6e 100644 --- a/src/login/logind.h +++ b/src/login/logind.h @@ -11,6 +11,7 @@ #include "hashmap.h" #include "list.h" #include "set.h" +#include "time-util.h" typedef struct Manager Manager; diff --git a/src/login/org.freedesktop.login1.conf b/src/login/org.freedesktop.login1.conf index f880f3e2d..f3c13ad89 100644 --- a/src/login/org.freedesktop.login1.conf +++ b/src/login/org.freedesktop.login1.conf @@ -190,6 +190,14 @@ send_interface="org.freedesktop.login1.Manager" send_member="CancelScheduledShutdown"/> + + + + @@ -198,6 +206,22 @@ send_interface="org.freedesktop.login1.Manager" send_member="SetRebootToFirmwareSetup"/> + + + + + + + + diff --git a/src/login/org.freedesktop.login1.policy b/src/login/org.freedesktop.login1.policy index 5ee62ab5b..6dc79aa32 100644 --- a/src/login/org.freedesktop.login1.policy +++ b/src/login/org.freedesktop.login1.policy @@ -337,8 +337,19 @@ + + Set the reboot "reason" in the kernel + Authentication is required to set the reboot "reason" in the kernel. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + - Allow indication to the firmware to boot to setup interface + Indicate to the firmware to boot to setup interface Authentication is required to indicate to the firmware to boot to setup interface. auth_admin_keep @@ -348,6 +359,28 @@ org.freedesktop.login1.reboot + + Indicate to the boot loader to boot to the boot loader menu + Authentication is required to indicate to the boot loader to boot to the boot loader menu. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + + + Indicate to the boot loader to boot a specific entry + Authentication is required to indicate to the boot loader to boot into a specific boot loader entry. + + auth_admin_keep + auth_admin_keep + yes + + org.freedesktop.login1.reboot + + Set a wall message Authentication is required to set a wall message diff --git a/src/login/pam_systemd.c b/src/login/pam_systemd.c index 997b74eb8..2bf3b7987 100644 --- a/src/login/pam_systemd.c +++ b/src/login/pam_systemd.c @@ -10,14 +10,17 @@ #include #include #include +#include +#include +#include #include "alloc-util.h" #include "audit-util.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-internal.h" #include "bus-util.h" #include "cgroup-util.h" -#include "def.h" #include "fd-util.h" #include "fileio.h" #include "format-util.h" @@ -31,7 +34,6 @@ #include "stdio-util.h" #include "strv.h" #include "terminal-util.h" -#include "util.h" static int parse_argv( pam_handle_t *handle, @@ -115,6 +117,15 @@ static int get_user_data( return PAM_SUCCESS; } +static bool display_is_local(const char *display) { + assert(display); + + return + display[0] == ':' && + display[1] >= '0' && + display[1] <= '9'; +} + static int socket_from_display(const char *display, char **path) { size_t k; char *f, *c; @@ -316,14 +327,21 @@ static const char* getenv_harder(pam_handle_t *handle, const char *key, const ch 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. */ + /* Looks for an environment variable, preferrably in the environment block associated with the + * specified PAM handle, falling back to the process' block instead. Why check both? Because we want + * to permit configuration of session properties from unit files that invoke PAM services, so that + * PAM services don't have to be reworked to set systemd-specific properties, but these properties + * can still be set from the unit file Environment= block. */ v = pam_getenv(handle, key); if (!isempty(v)) return v; - v = getenv(key); + /* We use secure_getenv() here, since we might get loaded into su/sudo, which are SUID. Ideally + * they'd clean up the environment before invoking foreign code (such as PAM modules), but alas they + * currently don't (to be precise, they clean up the environment they pass to their children, but + * not their own environ[]). */ + v = secure_getenv(key); if (!isempty(v)) return v; diff --git a/src/login/test-inhibit.c b/src/login/test-inhibit.c index a891b0af6..7f9013bf9 100644 --- a/src/login/test-inhibit.c +++ b/src/login/test-inhibit.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "sd-bus.h" @@ -30,7 +31,7 @@ static int inhibit(sd_bus *bus, const char *what) { assert_se(r >= 0); assert_se(fd >= 0); - return dup(fd); + return fcntl(fd, F_DUPFD_CLOEXEC, 3); } static void print_inhibitors(sd_bus *bus) { diff --git a/src/login/user-runtime-dir.c b/src/login/user-runtime-dir.c index eb66e18de..c8a562560 100644 --- a/src/login/user-runtime-dir.c +++ b/src/login/user-runtime-dir.c @@ -7,6 +7,7 @@ #include "bus-error.h" #include "fs-util.h" +#include "format-util.h" #include "label.h" #include "main-func.h" #include "mkdir.h" diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 7a558df89..1ee878055 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -29,6 +29,7 @@ #include "machine.h" #include "missing_capability.h" #include "mkdir.h" +#include "namespace-util.h" #include "os-util.h" #include "path-util.h" #include "process-util.h" diff --git a/src/machine/machine.c b/src/machine/machine.c index 4f89ac026..84454ddd8 100644 --- a/src/machine/machine.c +++ b/src/machine/machine.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "sd-messages.h" diff --git a/src/machine/machine.h b/src/machine/machine.h index 31527d029..9ff9a6567 100644 --- a/src/machine/machine.h +++ b/src/machine/machine.h @@ -7,6 +7,7 @@ typedef enum KillWho KillWho; #include "list.h" #include "machined.h" #include "operation.h" +#include "time-util.h" typedef enum MachineState { MACHINE_OPENING, /* Machine is being registered */ diff --git a/src/machine/machinectl.c b/src/machine/machinectl.c index 30f2e26a1..d98027a0c 100644 --- a/src/machine/machinectl.c +++ b/src/machine/machinectl.c @@ -18,8 +18,10 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-error.h" +#include "bus-unit-procs.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "bus-wait-for-jobs.h" #include "cgroup-show.h" #include "cgroup-util.h" #include "copy.h" @@ -35,6 +37,7 @@ #include "macro.h" #include "main-func.h" #include "mkdir.h" +#include "nulstr-util.h" #include "pager.h" #include "parse-util.h" #include "path-util.h" @@ -44,13 +47,13 @@ #include "rlimit-util.h" #include "sigbus.h" #include "signal-util.h" +#include "sort-util.h" #include "spawn-polkit-agent.h" #include "stdio-util.h" #include "string-table.h" #include "strv.h" #include "terminal-util.h" #include "unit-name.h" -#include "util.h" #include "verbs.h" #include "web-util.h" @@ -1990,16 +1993,6 @@ 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, *fn = NULL; @@ -2011,10 +2004,10 @@ static int import_tar(int argc, char *argv[], void *userdata) { assert(bus); if (argc >= 2) - path = nullify_dash(argv[1]); + path = empty_or_dash_to_null(argv[1]); if (argc >= 3) - local = nullify_dash(argv[2]); + local = empty_or_dash_to_null(argv[2]); else if (path) { r = path_extract_filename(path, &fn); if (r < 0) @@ -2078,10 +2071,10 @@ static int import_raw(int argc, char *argv[], void *userdata) { assert(bus); if (argc >= 2) - path = nullify_dash(argv[1]); + path = empty_or_dash_to_null(argv[1]); if (argc >= 3) - local = nullify_dash(argv[2]); + local = empty_or_dash_to_null(argv[2]); else if (path) { r = path_extract_filename(path, &fn); if (r < 0) @@ -2145,10 +2138,10 @@ static int import_fs(int argc, char *argv[], void *userdata) { assert(bus); if (argc >= 2) - path = nullify_dash(argv[1]); + path = empty_or_dash_to_null(argv[1]); if (argc >= 3) - local = nullify_dash(argv[2]); + local = empty_or_dash_to_null(argv[2]); else if (path) { r = path_extract_filename(path, &fn); if (r < 0) @@ -2227,8 +2220,7 @@ static int export_tar(int argc, char *argv[], void *userdata) { if (argc >= 3) path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); if (path) { determine_compression_from_filename(path); @@ -2277,8 +2269,7 @@ static int export_raw(int argc, char *argv[], void *userdata) { if (argc >= 3) path = argv[2]; - if (isempty(path) || streq(path, "-")) - path = NULL; + path = empty_or_dash_to_null(path); if (path) { determine_compression_from_filename(path); @@ -2335,8 +2326,7 @@ static int pull_tar(int argc, char *argv[], void *userdata) { local = l; } - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { r = tar_strip_suffixes(local, &ll); @@ -2399,8 +2389,7 @@ static int pull_raw(int argc, char *argv[], void *userdata) { local = l; } - if (isempty(local) || streq(local, "-")) - local = NULL; + local = empty_or_dash_to_null(local); if (local) { r = raw_strip_suffixes(local, &ll); @@ -2666,10 +2655,15 @@ static int clean_images(int argc, char *argv[], void *userdata) { return bus_log_parse_error(r); while ((r = sd_bus_message_read(reply, "(st)", &name, &usage)) > 0) { - log_info("Removed image '%s'. Freed exclusive disk space: %s", - name, format_bytes(fb, sizeof(fb), usage)); - - total += usage; + if (usage == UINT64_MAX) { + log_info("Removed image '%s'", name); + total = UINT64_MAX; + } else { + log_info("Removed image '%s'. Freed exclusive disk space: %s", + name, format_bytes(fb, sizeof(fb), usage)); + if (total != UINT64_MAX) + total += usage; + } c++; } @@ -2677,8 +2671,11 @@ static int clean_images(int argc, char *argv[], void *userdata) { if (r < 0) return bus_log_parse_error(r); - log_info("Removed %u images in total. Total freed exclusive disk space %s.", - c, format_bytes(fb, sizeof(fb), total)); + if (total == UINT64_MAX) + log_info("Removed %u images in total.", c); + else + log_info("Removed %u images in total. Total freed exclusive disk space: %s.", + c, format_bytes(fb, sizeof(fb), total)); return 0; } diff --git a/src/machine/machined.c b/src/machine/machined.c index 0b92b1c6e..a3bed035d 100644 --- a/src/machine/machined.c +++ b/src/machine/machined.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "sd-daemon.h" diff --git a/src/mount/mount-tool.c b/src/mount/mount-tool.c index bbbc91c38..b290095b0 100644 --- a/src/mount/mount-tool.c +++ b/src/mount/mount-tool.c @@ -8,11 +8,13 @@ #include "bus-error.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "bus-wait-for-jobs.h" #include "device-util.h" #include "dirent-util.h" #include "escape.h" #include "fd-util.h" #include "fileio.h" +#include "format-util.h" #include "fs-util.h" #include "fstab-util.h" #include "main-func.h" @@ -22,13 +24,14 @@ #include "parse-util.h" #include "path-util.h" #include "pretty-print.h" +#include "sort-util.h" #include "spawn-polkit-agent.h" #include "stat-util.h" #include "strv.h" +#include "terminal-util.h" #include "unit-def.h" #include "unit-name.h" #include "user-util.h" -#include "terminal-util.h" enum { ACTION_DEFAULT, diff --git a/src/network/fuzz-netdev-parser.c b/src/network/fuzz-netdev-parser.c index 37f7cf8cc..78c8d0c2b 100644 --- a/src/network/fuzz-netdev-parser.c +++ b/src/network/fuzz-netdev-parser.c @@ -18,7 +18,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size != 0) assert_se(fwrite(data, size, 1, f) == 1); - rewind(f); + fflush(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 index 4b7950080..b05626751 100644 --- a/src/network/fuzz-network-parser.c +++ b/src/network/fuzz-network-parser.c @@ -11,6 +11,9 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { _cleanup_fclose_ FILE *f = NULL; _cleanup_(unlink_tempfilep) char network_config[] = "/tmp/fuzz-networkd.XXXXXX"; + if (size > 65535) + return 0; + if (!getenv("SYSTEMD_LOG_LEVEL")) log_set_max_level(LOG_CRIT); @@ -18,7 +21,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { if (size != 0) assert_se(fwrite(data, size, 1, f) == 1); - rewind(f); + fflush(f); assert_se(manager_new(&manager) >= 0); (void) network_load_one(manager, network_config); return 0; diff --git a/src/network/fuzz-network-parser.options b/src/network/fuzz-network-parser.options new file mode 100644 index 000000000..0824b19fa --- /dev/null +++ b/src/network/fuzz-network-parser.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/src/network/meson.build b/src/network/meson.build index 8fe485442..c95e75030 100644 --- a/src/network/meson.build +++ b/src/network/meson.build @@ -37,6 +37,8 @@ sources = files(''' netdev/netdevsim.h netdev/fou-tunnel.c netdev/fou-tunnel.h + netdev/l2tp-tunnel.c + netdev/l2tp-tunnel.h networkd-address-label.c networkd-address-label.h networkd-address-pool.c diff --git a/src/network/netdev/bond.c b/src/network/netdev/bond.c index 550a7f891..7d912ae5a 100644 --- a/src/network/netdev/bond.c +++ b/src/network/netdev/bond.c @@ -116,7 +116,7 @@ static const char *const bond_arp_all_targets_table[_NETDEV_BOND_ARP_ALL_TARGETS DEFINE_STRING_TABLE_LOOKUP(bond_arp_all_targets, BondArpAllTargets); DEFINE_CONFIG_PARSE_ENUM(config_parse_bond_arp_all_targets, bond_arp_all_targets, BondArpAllTargets, "Failed to parse bond Arp all targets"); -static const char *bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = { +static const char *const bond_primary_reselect_table[_NETDEV_BOND_PRIMARY_RESELECT_MAX] = { [NETDEV_BOND_PRIMARY_RESELECT_ALWAYS] = "always", [NETDEV_BOND_PRIMARY_RESELECT_BETTER]= "better", [NETDEV_BOND_PRIMARY_RESELECT_FAILURE]= "failure", @@ -177,22 +177,21 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin assert(b); if (b->mode != _NETDEV_BOND_MODE_INVALID) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE, - bond_mode_to_kernel(b->mode)); + r = sd_netlink_message_append_u8(m, IFLA_BOND_MODE, bond_mode_to_kernel(b->mode)); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_MODE attribute: %m"); } if (b->xmit_hash_policy != _NETDEV_BOND_XMIT_HASH_POLICY_INVALID) { r = sd_netlink_message_append_u8(m, IFLA_BOND_XMIT_HASH_POLICY, - bond_xmit_hash_policy_to_kernel(b->xmit_hash_policy)); + bond_xmit_hash_policy_to_kernel(b->xmit_hash_policy)); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_XMIT_HASH_POLICY attribute: %m"); } if (b->lacp_rate != _NETDEV_BOND_LACP_RATE_INVALID && b->mode == NETDEV_BOND_MODE_802_3AD) { - r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate ); + r = sd_netlink_message_append_u8(m, IFLA_BOND_AD_LACP_RATE, b->lacp_rate); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_AD_LACP_RATE attribute: %m"); } @@ -220,8 +219,8 @@ static int netdev_bond_fill_message_create(NetDev *netdev, Link *link, sd_netlin if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_INTERVAL attribute: %m"); - if ((b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC) && - (b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC)) { + if (b->lp_interval >= LEARNING_PACKETS_INTERVAL_MIN_SEC && + b->lp_interval <= LEARNING_PACKETS_INTERVAL_MAX_SEC) { r = sd_netlink_message_append_u32(m, IFLA_BOND_LP_INTERVAL, b->lp_interval / USEC_PER_SEC); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_LP_INTERVAL attribute: %m"); @@ -313,23 +312,20 @@ 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_TLB_DYNAMIC_LB attribute: %m"); } - if (b->arp_interval > 0) { - if (b->n_arp_ip_targets > 0) { + if (b->arp_interval > 0 && b->n_arp_ip_targets > 0) { + r = sd_netlink_message_open_container(m, IFLA_BOND_ARP_IP_TARGET); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not open contaniner IFLA_BOND_ARP_IP_TARGET : %m"); - r = sd_netlink_message_open_container(m, IFLA_BOND_ARP_IP_TARGET); + LIST_FOREACH(arp_ip_target, target, b->arp_ip_targets) { + r = sd_netlink_message_append_u32(m, i++, target->ip.in.s_addr); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not open contaniner IFLA_BOND_ARP_IP_TARGET : %m"); - - LIST_FOREACH(arp_ip_target, target, b->arp_ip_targets) { - r = sd_netlink_message_append_u32(m, i++, target->ip.in.s_addr); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); - } - - r = sd_netlink_message_close_container(m); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not close contaniner IFLA_BOND_ARP_IP_TARGET : %m"); + return log_netdev_error_errno(netdev, r, "Could not append IFLA_BOND_ARP_ALL_TARGETS attribute: %m"); } + + r = sd_netlink_message_close_container(m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not close contaniner IFLA_BOND_ARP_IP_TARGET : %m"); } return 0; diff --git a/src/network/netdev/fou-tunnel.c b/src/network/netdev/fou-tunnel.c index 65dad384e..2d4481e06 100644 --- a/src/network/netdev/fou-tunnel.c +++ b/src/network/netdev/fou-tunnel.c @@ -5,8 +5,10 @@ #include #include "conf-parser.h" +#include "ip-protocol-list.h" #include "missing.h" #include "netdev/fou-tunnel.h" +#include "netlink-util.h" #include "networkd-link.h" #include "networkd-manager.h" #include "parse-util.h" @@ -27,6 +29,7 @@ DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDP 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; + uint8_t encap_type; int r; assert(netdev); @@ -43,7 +46,18 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r 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); + switch (t->fou_encap_type) { + case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT: + encap_type = FOU_ENCAP_DIRECT; + break; + case NETDEV_FOO_OVER_UDP_ENCAP_GUE: + encap_type = FOU_ENCAP_GUE; + break; + default: + assert_not_reached("invalid encap type"); + } + + r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m"); @@ -55,32 +69,84 @@ static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **r if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m"); - *ret = m; - m = NULL; - + *ret = TAKE_PTR(m); return 0; } +static int fou_tunnel_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); + if (r == -EEXIST) + log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "FooOverUDP tunnel is created"); + return 1; +} + 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); + assert(FOU(netdev)); 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"); + r = netlink_call_async(netdev->manager->genl, NULL, m, fou_tunnel_create_handler, + netdev_destroy_callback, netdev); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to create FooOverUDP tunnel: %m"); + netdev_ref(netdev); + return 0; +} + +int config_parse_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) { + + uint8_t *protocol = data; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + assert_cc(IPPROTO_MAX-1 <= UINT8_MAX); + + r = parse_ip_protocol(rvalue); + if (r < 0) { + r = safe_atou8(rvalue, protocol); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse IP protocol '%s' for Foo over UDP tunnel, " + "ignoring assignment: %m", rvalue); + return 0; + } + + *protocol = r; return 0; } @@ -94,14 +160,21 @@ static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) { 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; + switch (t->fou_encap_type) { + case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT: + if (t->fou_protocol <= 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "FooOverUDP protocol not configured in %s. Rejecting configuration.", + filename); + break; + case NETDEV_FOO_OVER_UDP_ENCAP_GUE: + if (t->fou_protocol > 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "FooOverUDP GUE can't be set with protocol configured in %s. Rejecting configuration.", + filename); + break; + default: + assert_not_reached("Invalid fou encap type"); } return 0; diff --git a/src/network/netdev/fou-tunnel.h b/src/network/netdev/fou-tunnel.h index b8abed19b..0e3fd7dd7 100644 --- a/src/network/netdev/fou-tunnel.h +++ b/src/network/netdev/fou-tunnel.h @@ -34,3 +34,4 @@ 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); +CONFIG_PARSER_PROTOTYPE(config_parse_ip_protocol); diff --git a/src/network/netdev/geneve.c b/src/network/netdev/geneve.c index 089bbfea2..0fb09961d 100644 --- a/src/network/netdev/geneve.c +++ b/src/network/netdev/geneve.c @@ -83,19 +83,16 @@ static int netdev_geneve_create(NetDev *netdev) { return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_ID attribute: %m"); } - if (!in_addr_is_null(v->remote_family, &v->remote)) { - + if (in_addr_is_null(v->remote_family, &v->remote) == 0) { if (v->remote_family == AF_INET) r = sd_netlink_message_append_in_addr(m, IFLA_GENEVE_REMOTE, &v->remote.in); else r = sd_netlink_message_append_in6_addr(m, IFLA_GENEVE_REMOTE6, &v->remote.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_GROUP attribute: %m"); - + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_REMOTE/IFLA_GENEVE_REMOTE6 attribute: %m"); } - if (v->ttl) { + if (v->ttl > 0) { r = sd_netlink_message_append_u8(m, IFLA_GENEVE_TTL, v->ttl); if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_GENEVE_TTL attribute: %m"); diff --git a/src/network/netdev/l2tp-tunnel.c b/src/network/netdev/l2tp-tunnel.c new file mode 100644 index 000000000..2a74d89e5 --- /dev/null +++ b/src/network/netdev/l2tp-tunnel.c @@ -0,0 +1,734 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include + +#include "sd-netlink.h" + +#include "conf-parser.h" +#include "hashmap.h" +#include "l2tp-tunnel.h" +#include "missing.h" +#include "netlink-util.h" +#include "networkd-address.h" +#include "networkd-manager.h" +#include "parse-util.h" +#include "socket-util.h" +#include "string-table.h" +#include "string-util.h" +#include "util.h" + +static const char* const l2tp_l2spec_type_table[_NETDEV_L2TP_L2SPECTYPE_MAX] = { + [NETDEV_L2TP_L2SPECTYPE_NONE] = "none", + [NETDEV_L2TP_L2SPECTYPE_DEFAULT] = "default", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(l2tp_l2spec_type, L2tpL2specType); + +static const char* const l2tp_encap_type_table[_NETDEV_L2TP_ENCAPTYPE_MAX] = { + [NETDEV_L2TP_ENCAPTYPE_UDP] = "udp", + [NETDEV_L2TP_ENCAPTYPE_IP] = "ip", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(l2tp_encap_type, L2tpEncapType); +DEFINE_CONFIG_PARSE_ENUM(config_parse_l2tp_encap_type, l2tp_encap_type, L2tpEncapType, "Failed to parse L2TP Encapsulation Type"); + +static const char* const l2tp_local_address_type_table[_NETDEV_L2TP_LOCAL_ADDRESS_MAX] = { + [NETDEV_L2TP_LOCAL_ADDRESS_AUTO] = "auto", + [NETDEV_L2TP_LOCAL_ADDRESS_STATIC] = "static", + [NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC] = "dynamic", +}; + +DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(l2tp_local_address_type, L2tpLocalAddressType); + +static void l2tp_session_free(L2tpSession *s) { + if (!s) + return; + + if (s->tunnel && s->section) + ordered_hashmap_remove(s->tunnel->sessions_by_section, s); + + network_config_section_free(s->section); + + free(s->name); + + free(s); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(L2tpSession, l2tp_session_free); + +static int l2tp_session_new_static(L2tpTunnel *t, const char *filename, unsigned section_line, L2tpSession **ret) { + _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(l2tp_session_freep) L2tpSession *s = NULL; + int r; + + assert(t); + assert(ret); + assert(filename); + assert(section_line > 0); + + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + s = ordered_hashmap_get(t->sessions_by_section, n); + if (s) { + *ret = TAKE_PTR(s); + return 0; + } + + s = new(L2tpSession, 1); + if (!s) + return -ENOMEM; + + *s = (L2tpSession) { + .l2tp_l2spec_type = NETDEV_L2TP_L2SPECTYPE_DEFAULT, + .tunnel = t, + .section = TAKE_PTR(n), + }; + + r = ordered_hashmap_ensure_allocated(&t->sessions_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = ordered_hashmap_put(t->sessions_by_section, s->section, s); + if (r < 0) + return r; + + *ret = TAKE_PTR(s); + return 0; +} + +static int netdev_l2tp_fill_message_tunnel(NetDev *netdev, union in_addr_union *local_address, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + uint16_t encap_type; + L2tpTunnel *t; + int r; + + assert(netdev); + assert(local_address); + + t = L2TP(netdev); + + assert(t); + + r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_TUNNEL_CREATE, &m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m"); + + r = sd_netlink_message_append_u32(m, L2TP_ATTR_CONN_ID, t->tunnel_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_CONN_ID attribute: %m"); + + r = sd_netlink_message_append_u32(m, L2TP_ATTR_PEER_CONN_ID, t->peer_tunnel_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PEER_CONN_ID attribute: %m"); + + r = sd_netlink_message_append_u8(m, L2TP_ATTR_PROTO_VERSION, 3); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PROTO_VERSION attribute: %m"); + + switch(t->l2tp_encap_type) { + case NETDEV_L2TP_ENCAPTYPE_IP: + encap_type = L2TP_ENCAPTYPE_IP; + break; + case NETDEV_L2TP_ENCAPTYPE_UDP: + default: + encap_type = L2TP_ENCAPTYPE_UDP; + break; + } + + r = sd_netlink_message_append_u16(m, L2TP_ATTR_ENCAP_TYPE, encap_type); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_ENCAP_TYPE attribute: %m"); + + if (t->family == AF_INET) { + r = sd_netlink_message_append_in_addr(m, L2TP_ATTR_IP_SADDR, &local_address->in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP_SADDR attribute: %m"); + + r = sd_netlink_message_append_in_addr(m, L2TP_ATTR_IP_DADDR, &t->remote.in); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP_DADDR attribute: %m"); + } else { + r = sd_netlink_message_append_in6_addr(m, L2TP_ATTR_IP6_SADDR, &local_address->in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP6_SADDR attribute: %m"); + + r = sd_netlink_message_append_in6_addr(m, L2TP_ATTR_IP6_DADDR, &t->remote.in6); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IP6_DADDR attribute: %m"); + } + + if (encap_type == L2TP_ENCAPTYPE_UDP) { + r = sd_netlink_message_append_u16(m, L2TP_ATTR_UDP_SPORT, t->l2tp_udp_sport); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_SPORT, attribute: %m"); + + r = sd_netlink_message_append_u16(m, L2TP_ATTR_UDP_DPORT, t->l2tp_udp_dport); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_DPORT attribute: %m"); + + if (t->udp_csum) { + r = sd_netlink_message_append_u8(m, L2TP_ATTR_UDP_CSUM, t->udp_csum); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_CSUM attribute: %m"); + } + + if (t->udp6_csum_tx) { + r = sd_netlink_message_append_flag(m, L2TP_ATTR_UDP_ZERO_CSUM6_TX); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_ZERO_CSUM6_TX attribute: %m"); + } + + if (t->udp6_csum_rx) { + r = sd_netlink_message_append_flag(m, L2TP_ATTR_UDP_ZERO_CSUM6_RX); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_UDP_ZERO_CSUM6_RX attribute: %m"); + } + } + + *ret = TAKE_PTR(m); + + return 0; +} + +static int netdev_l2tp_fill_message_session(NetDev *netdev, L2tpSession *session, sd_netlink_message **ret) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + uint16_t l2_spec_len; + uint8_t l2_spec_type; + int r; + + assert(netdev); + assert(session); + assert(session->tunnel); + + r = sd_genl_message_new(netdev->manager->genl, SD_GENL_L2TP, L2TP_CMD_SESSION_CREATE, &m); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to create generic netlink message: %m"); + + r = sd_netlink_message_append_u32(m, L2TP_ATTR_CONN_ID, session->tunnel->tunnel_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_CONN_ID attribute: %m"); + + r = sd_netlink_message_append_u32(m, L2TP_ATTR_PEER_CONN_ID, session->tunnel->peer_tunnel_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PEER_CONN_ID attribute: %m"); + + r = sd_netlink_message_append_u32(m, L2TP_ATTR_SESSION_ID, session->session_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_SESSION_ID attribute: %m"); + + r = sd_netlink_message_append_u32(m, L2TP_ATTR_PEER_SESSION_ID, session->peer_session_id); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PEER_SESSION_ID attribute: %m"); + + r = sd_netlink_message_append_u16(m, L2TP_ATTR_PW_TYPE, L2TP_PWTYPE_ETH); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_PW_TYPE attribute: %m"); + + switch (session->l2tp_l2spec_type) { + case NETDEV_L2TP_L2SPECTYPE_NONE: + l2_spec_type = L2TP_L2SPECTYPE_NONE; + l2_spec_len = 0; + break; + case NETDEV_L2TP_L2SPECTYPE_DEFAULT: + default: + l2_spec_type = L2TP_L2SPECTYPE_DEFAULT; + l2_spec_len = 4; + break; + } + + r = sd_netlink_message_append_u8(m, L2TP_ATTR_L2SPEC_TYPE, l2_spec_type); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_L2SPEC_TYPE attribute: %m"); + + r = sd_netlink_message_append_u8(m, L2TP_ATTR_L2SPEC_LEN, l2_spec_len); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_L2SPEC_LEN attribute: %m"); + + r = sd_netlink_message_append_string(m, L2TP_ATTR_IFNAME, session->name); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append L2TP_ATTR_IFNAME attribute: %m"); + + *ret = TAKE_PTR(m); + + return 0; +} + +static int l2tp_acquire_local_address_one(L2tpTunnel *t, Address *a, union in_addr_union *ret) { + if (a->family != t->family) + return -EINVAL; + + if (in_addr_is_null(a->family, &a->in_addr_peer) <= 0) + return -EINVAL; + + if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_STATIC && + !FLAGS_SET(a->flags, IFA_F_PERMANENT)) + return -EINVAL; + + if (t->local_address_type == NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC && + FLAGS_SET(a->flags, IFA_F_PERMANENT)) + return -EINVAL; + + *ret = a->in_addr; + return 0; +} + +static int l2tp_acquire_local_address(L2tpTunnel *t, Link *link, union in_addr_union *ret) { + Address *a; + Iterator i; + + assert(t); + assert(link); + assert(ret); + assert(IN_SET(t->family, AF_INET, AF_INET6)); + + if (!in_addr_is_null(t->family, &t->local)) { + /* local address is explicitly specified. */ + *ret = t->local; + return 0; + } + + SET_FOREACH(a, link->addresses, i) + if (l2tp_acquire_local_address_one(t, a, ret) >= 0) + return 1; + + SET_FOREACH(a, link->addresses_foreign, i) + if (l2tp_acquire_local_address_one(t, a, ret) >= 0) + return 1; + + return -ENODATA; +} + +static void l2tp_session_destroy_callback(L2tpSession *session) { + if (!session) + return; + + netdev_unref(NETDEV(session->tunnel)); +} + +static int l2tp_create_session_handler(sd_netlink *rtnl, sd_netlink_message *m, L2tpSession *session) { + NetDev *netdev; + int r; + + assert(session); + assert(session->tunnel); + + netdev = NETDEV(session->tunnel); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "L2TP session %s exists, using existing without changing its parameters", + session->name); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "L2TP session %s could not be created: %m", session->name); + return 1; + } + + log_netdev_debug(netdev, "L2TP session %s created", session->name); + return 1; +} + +static int l2tp_create_session(NetDev *netdev, L2tpSession *session) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *n = NULL; + int r; + + r = netdev_l2tp_fill_message_session(netdev, session, &n); + if (r < 0) + return r; + + r = netlink_call_async(netdev->manager->genl, NULL, n, l2tp_create_session_handler, + l2tp_session_destroy_callback, session); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to create L2TP session %s: %m", session->name); + + netdev_ref(netdev); + return 0; +} + +static int l2tp_create_tunnel_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) { + L2tpSession *session; + L2tpTunnel *t; + Iterator i; + int r; + + assert(netdev); + assert(netdev->state != _NETDEV_STATE_INVALID); + + t = L2TP(netdev); + + assert(t); + + r = sd_netlink_message_get_errno(m); + if (r == -EEXIST) + log_netdev_info(netdev, "netdev exists, using existing without changing its parameters"); + else if (r < 0) { + log_netdev_warning_errno(netdev, r, "netdev could not be created: %m"); + netdev_drop(netdev); + + return 1; + } + + log_netdev_debug(netdev, "L2TP tunnel is created"); + + ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section, i) + (void) l2tp_create_session(netdev, session); + + return 1; +} + +static int l2tp_create_tunnel(NetDev *netdev, Link *link) { + _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; + union in_addr_union local_address; + L2tpTunnel *t; + int r; + + assert(netdev); + + t = L2TP(netdev); + + assert(t); + + r = l2tp_acquire_local_address(t, link, &local_address); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not find local address."); + + if (r > 0 && DEBUG_LOGGING) { + _cleanup_free_ char *str = NULL; + + (void) in_addr_to_string(t->family, &local_address, &str); + log_netdev_debug(netdev, "Local address %s acquired.", strna(str)); + } + + r = netdev_l2tp_fill_message_tunnel(netdev, &local_address, &m); + if (r < 0) + return r; + + r = netlink_call_async(netdev->manager->genl, NULL, m, l2tp_create_tunnel_handler, + netdev_destroy_callback, netdev); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Failed to create L2TP tunnel: %m"); + + netdev_ref(netdev); + + return 0; +} + +int config_parse_l2tp_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) { + + L2tpTunnel *t = userdata; + union in_addr_union *addr = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (streq(lvalue, "Local")) { + L2tpLocalAddressType addr_type; + + if (isempty(rvalue)) + addr_type = NETDEV_L2TP_LOCAL_ADDRESS_AUTO; + else + addr_type = l2tp_local_address_type_from_string(rvalue); + + if (addr_type >= 0) { + if (in_addr_is_null(t->family, &t->remote) != 0) + /* If Remote= is not specified yet, then also clear family. */ + t->family = AF_UNSPEC; + + t->local = IN_ADDR_NULL; + t->local_address_type = addr_type; + + return 0; + } + } + + if (t->family == AF_UNSPEC) + r = in_addr_from_string_auto(rvalue, &t->family, addr); + else + r = in_addr_from_string(t->family, rvalue, addr); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid L2TP Tunnel address specified in %s='%s', ignoring assignment: %m", lvalue, rvalue); + return 0; + } + + return 0; +} + +int config_parse_l2tp_tunnel_id( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + uint32_t *id = data, k; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse L2TP tunnel id. Ignoring assignment: %s", rvalue); + return 0; + } + + if (k == 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid L2TP tunnel id. Ignoring assignment: %s", rvalue); + return 0; + } + + *id = k; + + return 0; +} + +int config_parse_l2tp_session_id( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(l2tp_session_free_or_set_invalidp) L2tpSession *session = NULL; + L2tpTunnel *t = userdata; + uint32_t k; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = l2tp_session_new_static(t, filename, section_line, &session); + if (r < 0) + return r; + + r = safe_atou32(rvalue, &k); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse L2TP session id. Ignoring assignment: %s", rvalue); + return 0; + } + + if (k == 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid L2TP session id. Ignoring assignment: %s", rvalue); + return 0; + } + + if (streq(lvalue, "SessionId")) + session->session_id = k; + else + session->peer_session_id = k; + + session = NULL; + return 0; +} + +int config_parse_l2tp_session_l2spec( + 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_(l2tp_session_free_or_set_invalidp) L2tpSession *session = NULL; + L2tpTunnel *t = userdata; + L2tpL2specType spec; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = l2tp_session_new_static(t, filename, section_line, &session); + if (r < 0) + return r; + + spec = l2tp_l2spec_type_from_string(rvalue); + if (spec < 0) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse layer2 specific header type. Ignoring assignment: %s", rvalue); + return 0; + } + + session->l2tp_l2spec_type = spec; + + session = NULL; + return 0; +} + +int config_parse_l2tp_session_name( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + _cleanup_(l2tp_session_free_or_set_invalidp) L2tpSession *session = NULL; + L2tpTunnel *t = userdata; + int r; + + assert(filename); + assert(section); + assert(lvalue); + assert(rvalue); + assert(data); + + r = l2tp_session_new_static(t, filename, section_line, &session); + if (r < 0) + return r; + + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse L2TP tunnel session name. Ignoring assignment: %s", rvalue); + return 0; + } + + r = free_and_strdup(&session->name, rvalue); + if (r < 0) + return log_oom(); + + session = NULL; + return 0; +} + +static void l2tp_tunnel_init(NetDev *netdev) { + L2tpTunnel *t; + + assert(netdev); + + t = L2TP(netdev); + + assert(t); + + t->l2tp_encap_type = NETDEV_L2TP_ENCAPTYPE_UDP; + t->udp6_csum_rx = true; + t->udp6_csum_tx = true; +} + +static int l2tp_session_verify(L2tpSession *session) { + NetDev *netdev; + + assert(session); + assert(session->tunnel); + + netdev = NETDEV(session->tunnel); + + if (section_is_invalid(session->section)) + return -EINVAL; + + if (!session->name) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: L2TP session without name configured. " + "Ignoring [L2TPSession] section from line %u", + session->section->filename, session->section->line); + + if (session->session_id == 0 || session->peer_session_id == 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: L2TP session without session IDs configured. " + "Ignoring [L2TPSession] section from line %u", + session->section->filename, session->section->line); + + return 0; +} + +static int netdev_l2tp_tunnel_verify(NetDev *netdev, const char *filename) { + L2tpTunnel *t; + L2tpSession *session; + Iterator i; + + assert(netdev); + assert(filename); + + t = L2TP(netdev); + + assert(t); + + if (!IN_SET(t->family, AF_INET, AF_INET6)) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: L2TP tunnel with invalid address family configured. Ignoring", + filename); + + if (in_addr_is_null(t->family, &t->remote)) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: L2TP tunnel without a remote address configured. Ignoring", + filename); + + if (t->tunnel_id == 0 || t->peer_tunnel_id == 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: L2TP tunnel without tunnel IDs configured. Ignoring", + filename); + + ORDERED_HASHMAP_FOREACH(session, t->sessions_by_section, i) + if (l2tp_session_verify(session) < 0) + l2tp_session_free(session); + + return 0; +} + +static void l2tp_tunnel_done(NetDev *netdev) { + L2tpTunnel *t; + + assert(netdev); + + t = L2TP(netdev); + + assert(t); + + ordered_hashmap_free_with_destructor(t->sessions_by_section, l2tp_session_free); +} + +const NetDevVTable l2tptnl_vtable = { + .object_size = sizeof(L2tpTunnel), + .init = l2tp_tunnel_init, + .sections = "Match\0NetDev\0L2TP\0L2TPSession\0", + .create_after_configured = l2tp_create_tunnel, + .done = l2tp_tunnel_done, + .create_type = NETDEV_CREATE_AFTER_CONFIGURED, + .config_verify = netdev_l2tp_tunnel_verify, +}; diff --git a/src/network/netdev/l2tp-tunnel.h b/src/network/netdev/l2tp-tunnel.h new file mode 100644 index 000000000..a97c924c5 --- /dev/null +++ b/src/network/netdev/l2tp-tunnel.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include + +#include "in-addr-util.h" +#include "netdev.h" +#include "networkd-util.h" + +typedef enum L2tpL2specType { + NETDEV_L2TP_L2SPECTYPE_NONE = L2TP_L2SPECTYPE_NONE, + NETDEV_L2TP_L2SPECTYPE_DEFAULT = L2TP_L2SPECTYPE_DEFAULT, + _NETDEV_L2TP_L2SPECTYPE_MAX, + _NETDEV_L2TP_L2SPECTYPE_INVALID = -1, +} L2tpL2specType; + +typedef enum L2tpEncapType { + NETDEV_L2TP_ENCAPTYPE_UDP = L2TP_ENCAPTYPE_UDP, + NETDEV_L2TP_ENCAPTYPE_IP = L2TP_ENCAPTYPE_IP, + _NETDEV_L2TP_ENCAPTYPE_MAX, + _NETDEV_L2TP_ENCAPTYPE_INVALID = -1, +} L2tpEncapType; + +typedef enum L2tpLocalAddressType { + NETDEV_L2TP_LOCAL_ADDRESS_AUTO, + NETDEV_L2TP_LOCAL_ADDRESS_STATIC, + NETDEV_L2TP_LOCAL_ADDRESS_DYNAMIC, + _NETDEV_L2TP_LOCAL_ADDRESS_MAX, + _NETDEV_L2TP_LOCAL_ADDRESS_INVALID = -1, +} L2tpLocalAddressType; + +typedef struct L2tpTunnel L2tpTunnel; + +typedef struct L2tpSession { + L2tpTunnel *tunnel; + NetworkConfigSection *section; + + char *name; + + uint32_t session_id; + uint32_t peer_session_id; + L2tpL2specType l2tp_l2spec_type; +} L2tpSession; + +struct L2tpTunnel { + NetDev meta; + + uint16_t l2tp_udp_sport; + uint16_t l2tp_udp_dport; + + uint32_t tunnel_id; + uint32_t peer_tunnel_id; + + int family; + + bool udp_csum; + bool udp6_csum_rx; + bool udp6_csum_tx; + + L2tpLocalAddressType local_address_type; + union in_addr_union local; + union in_addr_union remote; + + L2tpEncapType l2tp_encap_type; + + OrderedHashmap *sessions_by_section; +}; + +DEFINE_NETDEV_CAST(L2TP, L2tpTunnel); +extern const NetDevVTable l2tptnl_vtable; + +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_address); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_tunnel_id); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_encap_type); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_l2spec); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_id); +CONFIG_PARSER_PROTOTYPE(config_parse_l2tp_session_name); diff --git a/src/network/netdev/netdev-gperf.gperf b/src/network/netdev/netdev-gperf.gperf index f7ca98fa4..1a3d6caeb 100644 --- a/src/network/netdev/netdev-gperf.gperf +++ b/src/network/netdev/netdev-gperf.gperf @@ -20,6 +20,7 @@ _Pragma("GCC diagnostic ignored \"-Wimplicit-fallthrough\"") #include "netdev/vxcan.h" #include "netdev/wireguard.h" #include "netdev/fou-tunnel.h" +#include "netdev/l2tp-tunnel.h" #include "vlan-util.h" %} struct ConfigPerfItem; @@ -33,11 +34,11 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, match_kernel_cmdline) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, match_kernel_version) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, match_arch) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(NetDev, conditions) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(NetDev, conditions) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(NetDev, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(NetDev, conditions) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(NetDev, conditions) NetDev.Description, config_parse_string, 0, offsetof(NetDev, description) NetDev.Name, config_parse_ifname, 0, offsetof(NetDev, ifname) NetDev.Kind, config_parse_netdev_kind, 0, offsetof(NetDev, kind) @@ -72,11 +73,25 @@ Tunnel.FOUSourcePort, config_parse_ip_port, 0, 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.SerializeTunneledPackets, config_parse_tristate, 0, offsetof(Tunnel, gre_erspan_sequence) Tunnel.ISATAP, config_parse_tristate, 0, offsetof(Tunnel, isatap) -FooOverUDP.Protocol, config_parse_uint8, 0, offsetof(FouTunnel, fou_protocol) +FooOverUDP.Protocol, config_parse_ip_protocol, 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) +L2TP.TunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, tunnel_id) +L2TP.PeerTunnelId, config_parse_l2tp_tunnel_id, 0, offsetof(L2tpTunnel, peer_tunnel_id) +L2TP.UDPSourcePort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_sport) +L2TP.UDPDestinationPort, config_parse_ip_port, 0, offsetof(L2tpTunnel, l2tp_udp_dport) +L2TP.Local, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, local) +L2TP.Remote, config_parse_l2tp_tunnel_address, 0, offsetof(L2tpTunnel, remote) +L2TP.EncapsulationType, config_parse_l2tp_encap_type, 0, offsetof(L2tpTunnel, l2tp_encap_type) +L2TP.UDPCheckSum, config_parse_bool, 0, offsetof(L2tpTunnel, udp_csum) +L2TP.UDP6CheckSumRx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_rx) +L2TP.UDP6CheckSumTx, config_parse_bool, 0, offsetof(L2tpTunnel, udp6_csum_tx) +L2TPSession.SessionId, config_parse_l2tp_session_id, 0, 0 +L2TPSession.PeerSessionId, config_parse_l2tp_session_id, 0, 0 +L2TPSession.Layer2SpecificHeader, config_parse_l2tp_session_l2spec, 0, 0 +L2TPSession.Name, config_parse_l2tp_session_name, 0, 0 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) @@ -167,8 +182,10 @@ VRF.Table, config_parse_uint32, 0, WireGuard.FwMark, config_parse_unsigned, 0, offsetof(Wireguard, fwmark) WireGuard.ListenPort, config_parse_wireguard_listen_port, 0, offsetof(Wireguard, port) WireGuard.PrivateKey, config_parse_wireguard_private_key, 0, 0 +WireGuard.PrivateKeyFile, config_parse_wireguard_private_key_file, 0, 0 WireGuardPeer.AllowedIPs, config_parse_wireguard_allowed_ips, 0, 0 WireGuardPeer.Endpoint, config_parse_wireguard_endpoint, 0, 0 WireGuardPeer.PublicKey, config_parse_wireguard_public_key, 0, 0 WireGuardPeer.PresharedKey, config_parse_wireguard_preshared_key, 0, 0 +WireGuardPeer.PresharedKeyFile, config_parse_wireguard_preshared_key_file, 0, 0 WireGuardPeer.PersistentKeepalive, config_parse_wireguard_keepalive, 0, 0 diff --git a/src/network/netdev/netdev.c b/src/network/netdev/netdev.c index 026391746..c1bcfc66e 100644 --- a/src/network/netdev/netdev.c +++ b/src/network/netdev/netdev.c @@ -7,35 +7,35 @@ #include "conf-parser.h" #include "fd-util.h" #include "list.h" +#include "netdev/bond.h" +#include "netdev/bridge.h" +#include "netdev/dummy.h" +#include "netdev/fou-tunnel.h" +#include "netdev/geneve.h" +#include "netdev/ipvlan.h" +#include "netdev/l2tp-tunnel.h" +#include "netdev/macvlan.h" +#include "netdev/netdev.h" +#include "netdev/netdevsim.h" +#include "netdev/tunnel.h" +#include "netdev/tuntap.h" +#include "netdev/vcan.h" +#include "netdev/veth.h" +#include "netdev/vlan.h" +#include "netdev/vrf.h" +#include "netdev/vxcan.h" +#include "netdev/vxlan.h" +#include "netdev/wireguard.h" #include "netlink-util.h" #include "network-internal.h" -#include "netdev/netdev.h" -#include "networkd-manager.h" #include "networkd-link.h" +#include "networkd-manager.h" #include "siphash24.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" -#include "netdev/bridge.h" -#include "netdev/bond.h" -#include "netdev/geneve.h" -#include "netdev/vlan.h" -#include "netdev/macvlan.h" -#include "netdev/ipvlan.h" -#include "netdev/vxlan.h" -#include "netdev/tunnel.h" -#include "netdev/tuntap.h" -#include "netdev/veth.h" -#include "netdev/dummy.h" -#include "netdev/vrf.h" -#include "netdev/vcan.h" -#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, [NETDEV_KIND_BOND] = &bond_vtable, @@ -65,6 +65,7 @@ const NetDevVTable * const netdev_vtable[_NETDEV_KIND_MAX] = { [NETDEV_KIND_NETDEVSIM] = &netdevsim_vtable, [NETDEV_KIND_FOU] = &foutnl_vtable, [NETDEV_KIND_ERSPAN] = &erspan_vtable, + [NETDEV_KIND_L2TP] = &l2tptnl_vtable, }; static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { @@ -96,6 +97,7 @@ static const char* const netdev_kind_table[_NETDEV_KIND_MAX] = { [NETDEV_KIND_NETDEVSIM] = "netdevsim", [NETDEV_KIND_FOU] = "fou", [NETDEV_KIND_ERSPAN] = "erspan", + [NETDEV_KIND_L2TP] = "l2tp", }; DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind); @@ -172,12 +174,7 @@ static NetDev *netdev_free(NetDev *netdev) { free(netdev->description); free(netdev->ifname); free(netdev->mac); - - condition_free_list(netdev->match_host); - condition_free_list(netdev->match_virt); - condition_free_list(netdev->match_kernel_cmdline); - condition_free_list(netdev->match_kernel_version); - condition_free_list(netdev->match_arch); + condition_free_list(netdev->conditions); /* Invoke the per-kind done() destructor, but only if the state field is initialized. We conditionalize that * because we parse .netdev files twice: once to determine the kind (with a short, minimal NetDev structure @@ -598,6 +595,14 @@ static int netdev_create(NetDev *netdev, Link *link, link_netlink_message_handle return 0; } +static int netdev_create_after_configured(NetDev *netdev, Link *link) { + assert(netdev); + assert(link); + assert(NETDEV_VTABLE(netdev)->create_after_configured); + + return NETDEV_VTABLE(netdev)->create_after_configured(netdev, link); +} + /* the callback must be called, possibly after a timeout, as otherwise the Link will hang */ int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callback) { int r; @@ -605,9 +610,8 @@ int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callb assert(netdev); assert(netdev->manager); assert(netdev->manager->rtnl); - assert(NETDEV_VTABLE(netdev)); - switch (NETDEV_VTABLE(netdev)->create_type) { + switch (netdev_get_create_type(netdev)) { case NETDEV_CREATE_MASTER: r = netdev_enslave(netdev, link, callback); if (r < 0) @@ -620,6 +624,11 @@ int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t callb return r; break; + case NETDEV_CREATE_AFTER_CONFIGURED: + r = netdev_create_after_configured(netdev, link); + if (r < 0) + return r; + break; default: assert_not_reached("Can not join independent netdev"); } @@ -661,7 +670,7 @@ int netdev_load_one(Manager *manager, const char *filename) { }; dropin_dirname = strjoina(basename(filename), ".d"); - r = config_parse_many(filename, network_dirs, dropin_dirname, + r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, "Match\0NetDev\0", config_item_perf_lookup, network_netdev_gperf_lookup, CONFIG_PARSE_WARN|CONFIG_PARSE_RELAXED, netdev_raw); @@ -669,20 +678,18 @@ int netdev_load_one(Manager *manager, const char *filename) { return r; /* skip out early if configuration does not match the environment */ - if (net_match_config(NULL, NULL, NULL, NULL, NULL, - netdev_raw->match_host, netdev_raw->match_virt, - netdev_raw->match_kernel_cmdline, netdev_raw->match_kernel_version, - netdev_raw->match_arch, - NULL, NULL, NULL, NULL, NULL, NULL) <= 0) + if (!condition_test_list(netdev_raw->conditions, NULL, NULL, NULL)) { + log_debug("%s: Conditions in the file do not match the system environment, skipping.", filename); return 0; + } if (netdev_raw->kind == _NETDEV_KIND_INVALID) { - log_warning("NetDev has no Kind configured in %s. Ignoring", filename); + log_warning("NetDev has no Kind= configured in %s. Ignoring", filename); return 0; } if (!netdev_raw->ifname) { - log_warning("NetDev without Name configured in %s. Ignoring", filename); + log_warning("NetDev without Name= configured in %s. Ignoring", filename); return 0; } @@ -697,12 +704,13 @@ int netdev_load_one(Manager *manager, const char *filename) { netdev->n_ref = 1; netdev->manager = manager; netdev->kind = netdev_raw->kind; - netdev->state = NETDEV_STATE_LOADING; /* we initialize the state here for the first time, so that done() will be called on destruction */ + netdev->state = NETDEV_STATE_LOADING; /* we initialize the state here for the first time, + so that done() will be called on destruction */ if (NETDEV_VTABLE(netdev)->init) NETDEV_VTABLE(netdev)->init(netdev); - r = config_parse_many(filename, network_dirs, dropin_dirname, + 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); @@ -723,7 +731,9 @@ int netdev_load_one(Manager *manager, const char *filename) { if (!netdev->mac && netdev->kind != NETDEV_KIND_VLAN) { r = netdev_get_mac(netdev->ifname, &netdev->mac); if (r < 0) - return log_error_errno(r, "Failed to generate predictable MAC address for %s: %m", netdev->ifname); + return log_netdev_error_errno(netdev, r, + "Failed to generate predictable MAC address for %s: %m", + netdev->ifname); } r = hashmap_ensure_allocated(&netdev->manager->netdevs, &string_hash_ops); @@ -731,6 +741,19 @@ int netdev_load_one(Manager *manager, const char *filename) { return r; r = hashmap_put(netdev->manager->netdevs, netdev->ifname, netdev); + if (r == -EEXIST) { + NetDev *n = hashmap_get(netdev->manager->netdevs, netdev->ifname); + + assert(n); + log_netdev_warning_errno(netdev, r, + "The setting Name=%s in %s conflicts with the one in %s, ignoring", + netdev->ifname, netdev->filename, n->filename); + + /* Clear ifname before netdev_free() is called. Otherwise, the NetDev object 'n' is + * removed from the hashmap 'manager->netdevs'. */ + netdev->ifname = mfree(netdev->ifname); + return 0; + } if (r < 0) return r; @@ -738,16 +761,10 @@ int netdev_load_one(Manager *manager, const char *filename) { log_netdev_debug(netdev, "loaded %s", netdev_kind_to_string(netdev->kind)); - switch (NETDEV_VTABLE(netdev)->create_type) { - case NETDEV_CREATE_MASTER: - case NETDEV_CREATE_INDEPENDENT: + if (IN_SET(netdev_get_create_type(netdev), NETDEV_CREATE_MASTER, NETDEV_CREATE_INDEPENDENT)) { r = netdev_create(netdev, NULL, NULL); if (r < 0) return r; - - break; - default: - break; } switch (netdev->kind) { @@ -778,6 +795,9 @@ int netdev_load_one(Manager *manager, const char *filename) { case NETDEV_KIND_IP6TNL: independent = IP6TNL(netdev)->independent; break; + case NETDEV_KIND_ERSPAN: + independent = ERSPAN(netdev)->independent; + break; default: break; } @@ -802,11 +822,11 @@ int netdev_load(Manager *manager) { hashmap_clear_with_destructor(manager->netdevs, netdev_unref); - r = conf_files_list_strv(&files, ".netdev", NULL, 0, network_dirs); + r = conf_files_list_strv(&files, ".netdev", NULL, 0, NETWORK_DIRS); if (r < 0) return log_error_errno(r, "Failed to enumerate netdev files: %m"); - STRV_FOREACH_BACKWARDS(f, files) { + STRV_FOREACH(f, files) { r = netdev_load_one(manager, *f); if (r < 0) return r; diff --git a/src/network/netdev/netdev.h b/src/network/netdev/netdev.h index d6524da0f..ad4dd2e2b 100644 --- a/src/network/netdev/netdev.h +++ b/src/network/netdev/netdev.h @@ -46,7 +46,9 @@ typedef enum NetDevKind { NETDEV_KIND_NETDEVSIM, NETDEV_KIND_FOU, NETDEV_KIND_ERSPAN, + NETDEV_KIND_L2TP, _NETDEV_KIND_MAX, + _NETDEV_KIND_TUNNEL, /* Used by config_parse_stacked_netdev() */ _NETDEV_KIND_INVALID = -1 } NetDevKind; @@ -64,6 +66,7 @@ typedef enum NetDevCreateType { NETDEV_CREATE_INDEPENDENT, NETDEV_CREATE_MASTER, NETDEV_CREATE_STACKED, + NETDEV_CREATE_AFTER_CONFIGURED, _NETDEV_CREATE_MAX, _NETDEV_CREATE_INVALID = -1, } NetDevCreateType; @@ -78,11 +81,7 @@ typedef struct NetDev { char *filename; - Condition *match_host; - Condition *match_virt; - Condition *match_kernel_cmdline; - Condition *match_kernel_version; - Condition *match_arch; + LIST_HEAD(Condition, conditions); NetDevState state; NetDevKind kind; @@ -122,6 +121,9 @@ typedef struct NetDevVTable { /* create netdev, if not done via rtnl */ int (*create)(NetDev *netdev); + /* create netdev after link is fully configured */ + int (*create_after_configured)(NetDev *netdev, Link *link); + /* perform additional configuration after netdev has been createad */ int (*post_create)(NetDev *netdev, Link *link, sd_netlink_message *message); @@ -161,10 +163,19 @@ int netdev_get(Manager *manager, const char *name, NetDev **ret); int netdev_set_ifindex(NetDev *netdev, sd_netlink_message *newlink); int netdev_get_mac(const char *ifname, struct ether_addr **ret); int netdev_join(NetDev *netdev, Link *link, link_netlink_message_handler_t cb); +int netdev_join_after_configured(NetDev *netdev, Link *link, link_netlink_message_handler_t callback); const char *netdev_kind_to_string(NetDevKind d) _const_; NetDevKind netdev_kind_from_string(const char *d) _pure_; +static inline NetDevCreateType netdev_get_create_type(NetDev *netdev) { + assert(netdev); + assert(NETDEV_VTABLE(netdev)); + + return NETDEV_VTABLE(netdev)->create_type; +} + + CONFIG_PARSER_PROTOTYPE(config_parse_netdev_kind); /* gperf */ diff --git a/src/network/netdev/tunnel.c b/src/network/netdev/tunnel.c index 684edddb5..719b5e4ad 100644 --- a/src/network/netdev/tunnel.c +++ b/src/network/netdev/tunnel.c @@ -14,6 +14,7 @@ #include "conf-parser.h" #include "missing.h" +#include "netlink-util.h" #include "networkd-link.h" #include "netdev/tunnel.h" #include "parse-util.h" @@ -34,63 +35,20 @@ static const char* const ip6tnl_mode_table[_NETDEV_IP6_TNL_MODE_MAX] = { DEFINE_STRING_TABLE_LOOKUP(ip6tnl_mode, Ip6TnlMode); DEFINE_CONFIG_PARSE_ENUM(config_parse_ip6tnl_mode, ip6tnl_mode, Ip6TnlMode, "Failed to parse ip6 tunnel Mode"); -static int netdev_ipip_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = IPIP(netdev); +static int netdev_ipip_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { + Tunnel *t; int r; assert(netdev); + + if (netdev->kind == NETDEV_KIND_IPIP) + t = IPIP(netdev); + else + t = SIT(netdev); + assert(m); assert(t); - assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); - - if (link) { - r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - } - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_LOCAL, &t->local.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_IPTUN_REMOTE, &t->remote.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_TTL attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PMTUDISC, t->pmtudisc); - 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; -} - -static int netdev_sit_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = SIT(netdev); - int r; - - assert(netdev); - assert(m); - assert(t); - assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); + assert(t->family == AF_INET); if (link) { r = sd_netlink_message_append_u32(m, IFLA_IPTUN_LINK, link->ifindex); @@ -114,77 +72,49 @@ 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 (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_6RD_PREFIX attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_ENCAP_TYPE 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); + 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_6RD_PREFIXLEN attribute: %m"); + 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"); } - if (t->isatap >= 0) { - uint16_t flags = 0; + if (netdev->kind == NETDEV_KIND_SIT) { + 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"); - SET_FLAG(flags, SIT_ISATAP, t->isatap); + /* 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"); + } - 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"); + 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; } -static int netdev_gre_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t; - int r; - - assert(netdev); - - if (netdev->kind == NETDEV_KIND_GRE) - t = GRE(netdev); - else - t = GRETAP(netdev); - - assert(t); - assert(IN_SET(t->family, AF_INET, AF_UNSPEC)); - assert(m); - - if (link) { - r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK 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"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_TOS, t->tos); - if (r < 0) - log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TOS attribute: %m"); - - r = sd_netlink_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_PMTUDISC attribute: %m"); - - return r; -} - -static int netdev_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { +static int netdev_gre_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { uint32_t ikey = 0; uint32_t okey = 0; uint16_t iflags = 0; @@ -193,16 +123,56 @@ static int netdev_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netl 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); + switch (netdev->kind) { + case NETDEV_KIND_GRE: + t = GRE(netdev); + break; + case NETDEV_KIND_ERSPAN: + t = ERSPAN(netdev); + break; + case NETDEV_KIND_GRETAP: + t = GRETAP(netdev); + break; + default: + assert_not_reached("invalid netdev kind"); + } + + assert(t); + assert(t->family == AF_INET); + + if (link) { + r = sd_netlink_message_append_u32(m, IFLA_GRE_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_LINK attribute: %m"); + } + + if (netdev->kind == NETDEV_KIND_ERSPAN) { + 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"); + } + + 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_ERSPAN_INDEX attribute: %m"); + 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) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_REMOTE attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_TTL, t->ttl); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TTL attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_TOS, t->tos); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_TOS attribute: %m"); + + r = sd_netlink_message_append_u8(m, IFLA_GRE_PMTUDISC, t->pmtudisc); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_PMTUDISC attribute: %m"); if (t->key != 0) { ikey = okey = htobe32(t->key); @@ -220,10 +190,10 @@ static int netdev_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netl oflags |= GRE_KEY; } - if (t->erspan_sequence > 0) { + if (t->gre_erspan_sequence > 0) { iflags |= GRE_SEQ; oflags |= GRE_SEQ; - } else if (t->erspan_sequence == 0) { + } else if (t->gre_erspan_sequence == 0) { iflags &= ~GRE_SEQ; oflags &= ~GRE_SEQ; } @@ -244,13 +214,19 @@ static int netdev_erspan_fill_message_create(NetDev *netdev, Link *link, sd_netl 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"); + if (t->fou_tunnel) { + r = sd_netlink_message_append_u16(m, IFLA_GRE_ENCAP_TYPE, t->fou_encap_type); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ENCAP_TYPE 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"); + r = sd_netlink_message_append_u16(m, IFLA_GRE_ENCAP_SPORT, htobe16(t->encap_src_port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ENCAP_SPORT attribute: %m"); + + r = sd_netlink_message_append_u16(m, IFLA_GRE_ENCAP_DPORT, htobe16(t->fou_destination_port)); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_GRE_ENCAP_DPORT attribute: %m"); + } return r; } @@ -301,11 +277,12 @@ static int netdev_ip6gre_fill_message_create(NetDev *netdev, Link *link, sd_netl return r; } -static int netdev_vti_fill_message_key(NetDev *netdev, Link *link, sd_netlink_message *m) { +static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { uint32_t ikey, okey; Tunnel *t; int r; + assert(netdev); assert(m); if (netdev->kind == NETDEV_KIND_VTI) @@ -314,6 +291,14 @@ static int netdev_vti_fill_message_key(NetDev *netdev, Link *link, sd_netlink_me t = VTI6(netdev); assert(t); + assert((netdev->kind == NETDEV_KIND_VTI && t->family == AF_INET) || + (netdev->kind == NETDEV_KIND_VTI6 && t->family == AF_INET6)); + + if (link) { + r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); + if (r < 0) + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LINK attribute: %m"); + } if (t->key != 0) ikey = okey = htobe32(t->key); @@ -330,65 +315,13 @@ static int netdev_vti_fill_message_key(NetDev *netdev, Link *link, sd_netlink_me if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_OKEY attribute: %m"); - return 0; -} - -static int netdev_vti_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = VTI(netdev); - int r; - - assert(netdev); - assert(m); - assert(t); - assert(t->family == AF_INET); - - if (link) { - r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - } - - r = netdev_vti_fill_message_key(netdev, link, m); + r = netlink_message_append_in_addr_union(m, IFLA_VTI_LOCAL, t->family, &t->local); if (r < 0) - return r; + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_LOCAL attribute: %m"); - r = sd_netlink_message_append_in_addr(m, IFLA_VTI_LOCAL, &t->local.in); + r = netlink_message_append_in_addr_union(m, IFLA_VTI_REMOTE, t->family, &t->remote); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in_addr(m, IFLA_VTI_REMOTE, &t->remote.in); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); - - return r; -} - -static int netdev_vti6_fill_message_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Tunnel *t = VTI6(netdev); - int r; - - assert(netdev); - assert(m); - assert(t); - assert(t->family == AF_INET6); - - if (link) { - r = sd_netlink_message_append_u32(m, IFLA_VTI_LINK, link->ifindex); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LINK attribute: %m"); - } - - r = netdev_vti_fill_message_key(netdev, link, m); - if (r < 0) - return r; - - r = sd_netlink_message_append_in6_addr(m, IFLA_VTI_LOCAL, &t->local.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_LOCAL attribute: %m"); - - r = sd_netlink_message_append_in6_addr(m, IFLA_VTI_REMOTE, &t->remote.in6); - if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_REMOTE attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not append IFLA_VTI_REMOTE attribute: %m"); return r; } @@ -430,7 +363,7 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl if (t->copy_dscp) t->flags |= IP6_TNL_F_RCV_DSCP_COPY; - if (t->allow_localremote != -1) + if (t->allow_localremote >= 0) SET_FLAG(t->flags, IP6_TNL_F_ALLOW_LOCAL_REMOTE, t->allow_localremote); if (t->encap_limit != IPV6_DEFAULT_TNL_ENCAP_LIMIT) { @@ -458,7 +391,7 @@ static int netdev_ip6tnl_fill_message_create(NetDev *netdev, Link *link, sd_netl r = sd_netlink_message_append_u8(m, IFLA_IPTUN_PROTO, proto); if (r < 0) - return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_MODE attribute: %m"); + return log_netdev_error_errno(netdev, r, "Could not append IFLA_IPTUN_PROTO attribute: %m"); return r; } @@ -506,42 +439,37 @@ static int netdev_tunnel_verify(NetDev *netdev, const char *filename) { assert(t); - if (!IN_SET(t->family, AF_INET, AF_INET6, AF_UNSPEC)) { - log_netdev_error(netdev, - "Tunnel with invalid address family configured in %s. Ignoring", filename); - return -EINVAL; - } + if (IN_SET(netdev->kind, NETDEV_KIND_VTI, NETDEV_KIND_IPIP, NETDEV_KIND_SIT, NETDEV_KIND_GRE, NETDEV_KIND_GRETAP) && + t->family != AF_INET) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "vti/ipip/sit/gre tunnel without a local/remote IPv4 address configured in %s. Ignoring", filename); - 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/sit/gre/gretap/erspan tunnel without a local IPv4 address configured in %s. Ignoring", filename); - return -EINVAL; - } + if (IN_SET(netdev->kind, NETDEV_KIND_GRETAP, NETDEV_KIND_ERSPAN) && + (t->family != AF_INET || in_addr_is_null(t->family, &t->remote))) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "gretap/erspan tunnel without a remote IPv4 address configured in %s. Ignoring", filename); 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/ip6gretap tunnel without a local IPv6 address configured in %s. Ignoring", filename); - return -EINVAL; - } + t->family != AF_INET6) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "vti6/ip6tnl/ip6gre tunnel without a local/remote IPv6 address configured in %s. Ignoring", filename); + + if (netdev->kind == NETDEV_KIND_IP6GRETAP && + (t->family != AF_INET6 || in_addr_is_null(t->family, &t->remote))) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "ip6gretap tunnel without a remote IPv6 address configured in %s. Ignoring", filename); if (netdev->kind == NETDEV_KIND_IP6TNL && - t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) { - log_netdev_error(netdev, - "ip6tnl without mode configured in %s. Ignoring", filename); - return -EINVAL; - } + t->ip6tnl_mode == _NETDEV_IP6_TNL_MODE_INVALID) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "ip6tnl without mode configured in %s. Ignoring", filename); - if (t->fou_tunnel && t->fou_destination_port <= 0) { - log_netdev_error(netdev, "FooOverUDP missing port configured in %s. Ignoring", filename); - return -EINVAL; - } + if (t->fou_tunnel && t->fou_destination_port <= 0) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "FooOverUDP missing port configured in %s. Ignoring", filename); - 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; - } + if (netdev->kind == NETDEV_KIND_ERSPAN && (t->erspan_index >= (1 << 20) || t->erspan_index == 0)) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), "Invalid erspan index %d. Ignoring", t->erspan_index); return 0; } @@ -578,8 +506,8 @@ int config_parse_tunnel_address(const char *unit, * unspecified, also clear the address family. */ if (t->family != AF_UNSPEC && - in_addr_is_null(t->family, &t->local) && - in_addr_is_null(t->family, &t->remote)) + in_addr_is_null(t->family, &t->local) != 0 && + in_addr_is_null(t->family, &t->remote) != 0) t->family = AF_UNSPEC; return 0; } @@ -755,23 +683,26 @@ int config_parse_6rd_prefix(const char* unit, return 0; } -static void ipip_init(NetDev *n) { - Tunnel *t = IPIP(n); +static void ipip_sit_init(NetDev *n) { + Tunnel *t; assert(n); + + switch (n->kind) { + case NETDEV_KIND_IPIP: + t = IPIP(n); + break; + case NETDEV_KIND_SIT: + t = SIT(n); + break; + default: + assert_not_reached("invalid netdev kind"); + } + assert(t); t->pmtudisc = true; t->fou_encap_type = FOU_ENCAP_DIRECT; -} - -static void sit_init(NetDev *n) { - Tunnel *t = SIT(n); - - assert(n); - assert(t); - - t->pmtudisc = true; t->isatap = -1; } @@ -790,19 +721,30 @@ static void vti_init(NetDev *n) { t->pmtudisc = true; } -static void gre_init(NetDev *n) { +static void gre_erspan_init(NetDev *n) { Tunnel *t; assert(n); - if (n->kind == NETDEV_KIND_GRE) + switch (n->kind) { + case NETDEV_KIND_GRE: t = GRE(n); - else + break; + case NETDEV_KIND_ERSPAN: + t = ERSPAN(n); + break; + case NETDEV_KIND_GRETAP: t = GRETAP(n); + break; + default: + assert_not_reached("invalid netdev kind"); + } assert(t); t->pmtudisc = true; + t->gre_erspan_sequence = -1; + t->fou_encap_type = FOU_ENCAP_DIRECT; } static void ip6gre_init(NetDev *n) { @@ -820,18 +762,6 @@ 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); @@ -847,18 +777,18 @@ static void ip6tnl_init(NetDev *n) { const NetDevVTable ipip_vtable = { .object_size = sizeof(Tunnel), - .init = ipip_init, + .init = ipip_sit_init, .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_ipip_fill_message_create, + .fill_message_create = netdev_ipip_sit_fill_message_create, .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; const NetDevVTable sit_vtable = { .object_size = sizeof(Tunnel), - .init = sit_init, + .init = ipip_sit_init, .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_sit_fill_message_create, + .fill_message_create = netdev_ipip_sit_fill_message_create, .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; @@ -876,25 +806,25 @@ const NetDevVTable vti6_vtable = { .object_size = sizeof(Tunnel), .init = vti_init, .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_vti6_fill_message_create, + .fill_message_create = netdev_vti_fill_message_create, .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; const NetDevVTable gre_vtable = { .object_size = sizeof(Tunnel), - .init = gre_init, + .init = gre_erspan_init, .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_gre_fill_message_create, + .fill_message_create = netdev_gre_erspan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; const NetDevVTable gretap_vtable = { .object_size = sizeof(Tunnel), - .init = gre_init, + .init = gre_erspan_init, .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_gre_fill_message_create, + .fill_message_create = netdev_gre_erspan_fill_message_create, .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; @@ -928,9 +858,9 @@ const NetDevVTable ip6tnl_vtable = { const NetDevVTable erspan_vtable = { .object_size = sizeof(Tunnel), - .init = erspan_init, + .init = gre_erspan_init, .sections = "Match\0NetDev\0Tunnel\0", - .fill_message_create = netdev_erspan_fill_message_create, - .create_type = NETDEV_CREATE_INDEPENDENT, + .fill_message_create = netdev_gre_erspan_fill_message_create, + .create_type = NETDEV_CREATE_STACKED, .config_verify = netdev_tunnel_verify, }; diff --git a/src/network/netdev/tunnel.h b/src/network/netdev/tunnel.h index 8f511dd1f..3637e4f37 100644 --- a/src/network/netdev/tunnel.h +++ b/src/network/netdev/tunnel.h @@ -29,7 +29,7 @@ typedef struct Tunnel { int family; int ipv6_flowlabel; int allow_localremote; - int erspan_sequence; + int gre_erspan_sequence; int isatap; unsigned ttl; diff --git a/src/network/netdev/vxlan.c b/src/network/netdev/vxlan.c index 4cb2eca3d..4b855ae1e 100644 --- a/src/network/netdev/vxlan.c +++ b/src/network/netdev/vxlan.c @@ -33,24 +33,20 @@ static int netdev_vxlan_fill_message_create(NetDev *netdev, Link *link, sd_netli return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_ID attribute: %m"); } - if (!in_addr_is_null(v->remote_family, &v->remote)) { - + if (in_addr_is_null(v->remote_family, &v->remote) == 0) { if (v->remote_family == AF_INET) r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_GROUP, &v->remote.in); else r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_GROUP6, &v->remote.in6); - 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)) { - + if (in_addr_is_null(v->local_family, &v->local) == 0) { if (v->local_family == AF_INET) r = sd_netlink_message_append_in_addr(m, IFLA_VXLAN_LOCAL, &v->local.in); else r = sd_netlink_message_append_in6_addr(m, IFLA_VXLAN_LOCAL6, &v->local.in6); - if (r < 0) return log_netdev_error_errno(netdev, r, "Could not append IFLA_VXLAN_LOCAL attribute: %m"); } diff --git a/src/network/netdev/wireguard.c b/src/network/netdev/wireguard.c index 0c0b16d1d..7959c1c01 100644 --- a/src/network/netdev/wireguard.c +++ b/src/network/netdev/wireguard.c @@ -9,40 +9,100 @@ #include "sd-resolve.h" #include "alloc-util.h" +#include "event-util.h" #include "fd-util.h" +#include "fileio.h" #include "hexdecoct.h" +#include "memory-util.h" +#include "netlink-util.h" #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-util.h" #include "parse-util.h" +#include "path-util.h" #include "resolve-private.h" #include "string-util.h" #include "strv.h" -#include "wireguard.h" #include "wireguard-netlink.h" +#include "wireguard.h" static void resolve_endpoints(NetDev *netdev); -static WireguardPeer *wireguard_peer_new(Wireguard *w, unsigned section) { - WireguardPeer *peer; +static void wireguard_peer_free(WireguardPeer *peer) { + WireguardIPmask *mask; + + if (!peer) + return; + + if (peer->wireguard) { + LIST_REMOVE(peers, peer->wireguard->peers, peer); + + set_remove(peer->wireguard->peers_with_unresolved_endpoint, peer); + set_remove(peer->wireguard->peers_with_failed_endpoint, peer); + + if (peer->section) + hashmap_remove(peer->wireguard->peers_by_section, peer->section); + } + + network_config_section_free(peer->section); + + while ((mask = peer->ipmasks)) { + LIST_REMOVE(ipmasks, peer->ipmasks, mask); + free(mask); + } + + free(peer->endpoint_host); + free(peer->endpoint_port); + free(peer->preshared_key_file); + explicit_bzero_safe(peer->preshared_key, WG_KEY_LEN); + + free(peer); +} + +DEFINE_NETWORK_SECTION_FUNCTIONS(WireguardPeer, wireguard_peer_free); + +static int wireguard_peer_new_static(Wireguard *w, const char *filename, unsigned section_line, WireguardPeer **ret) { + _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; + _cleanup_(wireguard_peer_freep) WireguardPeer *peer = NULL; + int r; assert(w); + assert(ret); + assert(filename); + assert(section_line > 0); - if (w->last_peer_section == section && w->peers) - return w->peers; + r = network_config_section_new(filename, section_line, &n); + if (r < 0) + return r; + + peer = hashmap_get(w->peers_by_section, n); + if (peer) { + *ret = TAKE_PTR(peer); + return 0; + } peer = new(WireguardPeer, 1); if (!peer) - return NULL; + return -ENOMEM; *peer = (WireguardPeer) { .flags = WGPEER_F_REPLACE_ALLOWEDIPS, + .wireguard = w, + .section = TAKE_PTR(n), }; LIST_PREPEND(peers, w->peers, peer); - w->last_peer_section = section; - return peer; + r = hashmap_ensure_allocated(&w->peers_by_section, &network_config_hash_ops); + if (r < 0) + return r; + + r = hashmap_put(w->peers_by_section, peer->section, peer); + if (r < 0) + return r; + + *ret = TAKE_PTR(peer); + return 0; } static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, const WireguardIPmask *mask, uint16_t index) { @@ -62,10 +122,7 @@ static int wireguard_set_ipmask_one(NetDev *netdev, sd_netlink_message *message, if (r < 0) goto cancel; - if (mask->family == AF_INET) - r = sd_netlink_message_append_in_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in); - else if (mask->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(message, WGALLOWEDIP_A_IPADDR, &mask->ip.in6); + r = netlink_message_append_in_addr_union(message, WGALLOWEDIP_A_IPADDR, mask->family, &mask->ip); if (r < 0) goto cancel; @@ -122,12 +179,11 @@ static int wireguard_set_peer_one(NetDev *netdev, sd_netlink_message *message, c if (r < 0) goto cancel; - if (peer->endpoint.sa.sa_family == AF_INET) - r = sd_netlink_message_append_sockaddr_in(message, WGPEER_A_ENDPOINT, &peer->endpoint.in); - else if (peer->endpoint.sa.sa_family == AF_INET6) - r = sd_netlink_message_append_sockaddr_in6(message, WGPEER_A_ENDPOINT, &peer->endpoint.in6); - if (r < 0) - goto cancel; + if (IN_SET(peer->endpoint.sa.sa_family, AF_INET, AF_INET6)) { + r = netlink_message_append_sockaddr_union(message, WGPEER_A_ENDPOINT, &peer->endpoint); + if (r < 0) + goto cancel; + } } r = sd_netlink_message_open_container(message, WGPEER_A_ALLOWEDIPS); @@ -229,24 +285,20 @@ static int wireguard_set_interface(NetDev *netdev) { return 0; } -static WireguardEndpoint* wireguard_endpoint_free(WireguardEndpoint *e) { - if (!e) - return NULL; - e->host = mfree(e->host); - e->port = mfree(e->port); - return mfree(e); +static void wireguard_peer_destroy_callback(WireguardPeer *peer) { + NetDev *netdev; + + assert(peer); + assert(peer->wireguard); + + netdev = NETDEV(peer->wireguard); + + if (section_is_invalid(peer->section)) + wireguard_peer_free(peer); + + netdev_unref(netdev); } -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) { NetDev *netdev = userdata; Wireguard *w; @@ -258,8 +310,9 @@ static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { if (!netdev_is_managed(netdev)) return 0; - assert(!w->unresolved_endpoints); - w->unresolved_endpoints = TAKE_PTR(w->failed_endpoints); + assert(set_isempty(w->peers_with_unresolved_endpoint)); + + SWAP_TWO(w->peers_with_unresolved_endpoint, w->peers_with_failed_endpoint); resolve_endpoints(netdev); @@ -271,69 +324,70 @@ static int on_resolve_retry(sd_event_source *s, usec_t usec, void *userdata) { * increasing time in milliseconds to wait starting at 200ms and capped at 25 seconds. */ static int exponential_backoff_milliseconds(unsigned n_retries) { - return (2 << MAX(n_retries, 7U)) * 100 * USEC_PER_MSEC; + return (2 << MIN(n_retries, 7U)) * 100 * USEC_PER_MSEC; } static int wireguard_resolve_handler(sd_resolve_query *q, int ret, const struct addrinfo *ai, - WireguardEndpoint *e) { - _cleanup_(netdev_unrefp) NetDev *netdev_will_unrefed = NULL; + WireguardPeer *peer) { NetDev *netdev; Wireguard *w; int r; - assert(e); - assert(e->netdev); + assert(peer); + assert(peer->wireguard); - netdev = e->netdev; - w = WIREGUARD(netdev); - assert(w); + w = peer->wireguard; + netdev = NETDEV(w); if (!netdev_is_managed(netdev)) 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); - (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. */ + log_netdev_error(netdev, "Failed to resolve host '%s:%s': %s", peer->endpoint_host, peer->endpoint_port, gai_strerror(ret)); + + r = set_ensure_allocated(&w->peers_with_failed_endpoint, NULL); + if (r < 0) { + log_oom(); + peer->section->invalid = true; + goto resolve_next; + } + + r = set_put(w->peers_with_failed_endpoint, peer); + if (r < 0) { + log_netdev_error(netdev, "Failed to save a peer, dropping the peer: %m"); + peer->section->invalid = true; + goto resolve_next; + } + } else if ((ai->ai_family == AF_INET && ai->ai_addrlen == sizeof(struct sockaddr_in)) || (ai->ai_family == AF_INET6 && ai->ai_addrlen == sizeof(struct sockaddr_in6))) - memcpy(&e->peer->endpoint, ai->ai_addr, ai->ai_addrlen); + memcpy(&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); + log_netdev_error(netdev, "Neither IPv4 nor IPv6 address found for peer endpoint %s:%s, ignoring the address.", + peer->endpoint_host, peer->endpoint_port); - if (w->unresolved_endpoints) { +resolve_next: + if (!set_isempty(w->peers_with_unresolved_endpoint)) { resolve_endpoints(netdev); return 0; } (void) wireguard_set_interface(netdev); - if (w->failed_endpoints) { - _cleanup_(sd_event_source_unrefp) sd_event_source *s = NULL; + + if (!set_isempty(w->peers_with_failed_endpoint)) { + usec_t usec; w->n_retries++; - r = sd_event_add_time(netdev->manager->event, - &s, - CLOCK_MONOTONIC, - now(CLOCK_MONOTONIC) + exponential_backoff_milliseconds(w->n_retries), - 0, - on_resolve_retry, - netdev); + usec = usec_add(now(CLOCK_MONOTONIC), exponential_backoff_milliseconds(w->n_retries)); + r = event_reset_time(netdev->manager->event, &w->resolve_retry_event_source, + CLOCK_MONOTONIC, usec, 0, on_resolve_retry, netdev, + 0, "wireguard-resolve-retry", true); 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; @@ -345,24 +399,24 @@ static void resolve_endpoints(NetDev *netdev) { .ai_socktype = SOCK_DGRAM, .ai_protocol = IPPROTO_UDP }; - WireguardEndpoint *endpoint; + WireguardPeer *peer; Wireguard *w; + Iterator i; int r = 0; assert(netdev); w = WIREGUARD(netdev); assert(w); - LIST_FOREACH(endpoints, endpoint, w->unresolved_endpoints) { + SET_FOREACH(peer, w->peers_with_unresolved_endpoint, i) { r = resolve_getaddrinfo(netdev->manager->resolve, NULL, - endpoint->host, - endpoint->port, + peer->endpoint_host, + peer->endpoint_port, &hints, wireguard_resolve_handler, - wireguard_endpoint_destroy_callback, - endpoint); - + wireguard_peer_destroy_callback, + peer); if (r == -ENOBUFS) break; if (r < 0) { @@ -373,32 +427,31 @@ static void resolve_endpoints(NetDev *netdev) { /* Avoid freeing netdev. It will be unrefed by the destroy callback. */ netdev_ref(netdev); - LIST_REMOVE(endpoints, w->unresolved_endpoints, endpoint); + (void) set_remove(w->peers_with_unresolved_endpoint, peer); } } static int netdev_wireguard_post_create(NetDev *netdev, Link *link, sd_netlink_message *m) { - Wireguard *w; - assert(netdev); - w = WIREGUARD(netdev); - assert(w); + assert(WIREGUARD(netdev)); (void) wireguard_set_interface(netdev); resolve_endpoints(netdev); return 0; } -int config_parse_wireguard_listen_port(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +int config_parse_wireguard_listen_port( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + uint16_t *s = data; uint16_t port = 0; int r; @@ -407,175 +460,249 @@ int config_parse_wireguard_listen_port(const char *unit, assert(data); if (!streq(rvalue, "auto")) { - r = parse_ip_port(rvalue, &port); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid port specification, ignoring assignment: %s", rvalue); + r = parse_ip_port(rvalue, s); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid port specification, ignoring assignment: %s", rvalue); + return 0; + } } *s = port; - return 0; } -static int parse_wireguard_key(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +static int wireguard_decode_key_and_warn( + const char *rvalue, + uint8_t ret[static WG_KEY_LEN], + const char *unit, + const char *filename, + unsigned line, + const char *lvalue) { + _cleanup_free_ void *key = NULL; size_t len; int r; - assert(filename); assert(rvalue); - assert(userdata); + assert(ret); + assert(filename); + assert(lvalue); - r = unbase64mem(rvalue, strlen(rvalue), &key, &len); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse wireguard key \"%s\", ignoring assignment: %m", rvalue); + if (isempty(rvalue)) { + memzero(ret, WG_KEY_LEN); return 0; } + + if (!streq(lvalue, "PublicKey")) + (void) warn_file_is_world_accessible(filename, NULL, unit, line); + + r = unbase64mem_full(rvalue, strlen(rvalue), true, &key, &len); + if (r < 0) + return log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to decode wireguard key provided by %s=, ignoring assignment: %m", lvalue); if (len != WG_KEY_LEN) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Wireguard key is too short, ignoring assignment: %s", rvalue); + explicit_bzero_safe(key, len); + return log_syntax(unit, LOG_ERR, filename, line, 0, + "Wireguard key provided by %s= has invalid length (%zu bytes), ignoring assignment.", + lvalue, len); + } + + memcpy(ret, key, WG_KEY_LEN); + return 0; +} + +int config_parse_wireguard_private_key( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Wireguard *w; + + assert(data); + w = WIREGUARD(data); + assert(w); + + (void) wireguard_decode_key_and_warn(rvalue, w->private_key, unit, filename, line, lvalue); + return 0; +} + +int config_parse_wireguard_private_key_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 *path = NULL; + Wireguard *w; + + assert(data); + w = WIREGUARD(data); + assert(w); + + if (isempty(rvalue)) { + w->private_key_file = mfree(w->private_key_file); return 0; } - memcpy(userdata, key, WG_KEY_LEN); - return true; -} - -int config_parse_wireguard_private_key(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Wireguard *w; - - assert(data); - - w = WIREGUARD(data); - - assert(w); - - return parse_wireguard_key(unit, - filename, - line, - section, - section_line, - lvalue, - ltype, - rvalue, - data, - &w->private_key); - -} - -int config_parse_wireguard_preshared_key(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - Wireguard *w; - WireguardPeer *peer; - - assert(data); - - w = WIREGUARD(data); - - assert(w); - - peer = wireguard_peer_new(w, section_line); - if (!peer) + path = strdup(rvalue); + if (!path) return log_oom(); - return parse_wireguard_key(unit, - filename, - line, - section, - section_line, - lvalue, - ltype, - rvalue, - data, - peer->preshared_key); + if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0) + return 0; + + return free_and_replace(w->private_key_file, path); } -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_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) { + + _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; Wireguard *w; - WireguardPeer *peer; + int r; assert(data); - w = WIREGUARD(data); - assert(w); - peer = wireguard_peer_new(w, section_line); - if (!peer) - return log_oom(); + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return r; - return parse_wireguard_key(unit, - filename, - line, - section, - section_line, - lvalue, - ltype, - rvalue, - data, - peer->public_key); + r = wireguard_decode_key_and_warn(rvalue, peer->preshared_key, unit, filename, line, lvalue); + if (r < 0) + return r; + + TAKE_PTR(peer); + return 0; } -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_preshared_key_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_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; + _cleanup_free_ char *path = NULL; + Wireguard *w; + int r; + + assert(data); + w = WIREGUARD(data); + assert(w); + + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return r; + + if (isempty(rvalue)) { + peer->preshared_key_file = mfree(peer->preshared_key_file); + TAKE_PTR(peer); + return 0; + } + + path = strdup(rvalue); + if (!path) + return log_oom(); + + if (path_simplify_and_warn(path, PATH_CHECK_ABSOLUTE, unit, filename, line, lvalue) < 0) + return 0; + + free_and_replace(peer->preshared_key_file, path); + TAKE_PTR(peer); + return 0; +} + +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) { + + _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; + Wireguard *w; + int r; + + assert(data); + w = WIREGUARD(data); + assert(w); + + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return r; + + r = wireguard_decode_key_and_warn(rvalue, peer->public_key, unit, filename, line, lvalue); + if (r < 0) + return r; + + TAKE_PTR(peer); + return 0; +} + +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) { + + _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; union in_addr_union addr; unsigned char prefixlen; int r, family; Wireguard *w; - WireguardPeer *peer; WireguardIPmask *ipmask; assert(rvalue); assert(data); w = WIREGUARD(data); + assert(w); - peer = wireguard_peer_new(w, section_line); - if (!peer) - return log_oom(); + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return r; for (;;) { _cleanup_free_ char *word = NULL; @@ -586,14 +713,16 @@ int config_parse_wireguard_allowed_ips(const char *unit, if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split allowed ips \"%s\" option: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to split allowed ips \"%s\" option: %m", rvalue); break; } r = in_addr_prefix_from_string_auto(word, &family, &addr, &prefixlen); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Network address is invalid, ignoring assignment: %s", word); - return 0; + log_syntax(unit, LOG_ERR, filename, line, r, + "Network address is invalid, ignoring assignment: %s", word); + continue; } ipmask = new(WireguardIPmask, 1); @@ -609,48 +738,53 @@ int config_parse_wireguard_allowed_ips(const char *unit, LIST_PREPEND(ipmasks, peer->ipmasks, ipmask); } + TAKE_PTR(peer); return 0; } -int config_parse_wireguard_endpoint(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { +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) { + + _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; + const char *begin, *end; Wireguard *w; - WireguardPeer *peer; size_t len; - const char *begin, *end = NULL; - _cleanup_free_ char *host = NULL, *port = NULL; - _cleanup_(wireguard_endpoint_freep) WireguardEndpoint *endpoint = NULL; + int r; assert(data); assert(rvalue); w = WIREGUARD(data); - assert(w); - peer = wireguard_peer_new(w, section_line); - if (!peer) - return log_oom(); + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return r; if (rvalue[0] == '[') { begin = &rvalue[1]; end = strchr(rvalue, ']'); if (!end) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find matching brace of endpoint, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Unable to find matching brace of endpoint, ignoring assignment: %s", + rvalue); return 0; } len = end - begin; ++end; if (*end != ':' || !*(end + 1)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Unable to find port of endpoint, ignoring assignment: %s", + rvalue); return 0; } ++end; @@ -658,71 +792,77 @@ int config_parse_wireguard_endpoint(const char *unit, begin = rvalue; end = strrchr(rvalue, ':'); if (!end || !*(end + 1)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Unable to find port of endpoint: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Unable to find port of endpoint, ignoring assignment: %s", + rvalue); return 0; } len = end - begin; ++end; } - host = strndup(begin, len); - if (!host) + r = free_and_strndup(&peer->endpoint_host, begin, len); + if (r < 0) return log_oom(); - port = strdup(end); - if (!port) + r = free_and_strdup(&peer->endpoint_port, end); + if (r < 0) return log_oom(); - endpoint = new(WireguardEndpoint, 1); - if (!endpoint) + r = set_ensure_allocated(&w->peers_with_unresolved_endpoint, NULL); + if (r < 0) 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)); + r = set_put(w->peers_with_unresolved_endpoint, peer); + if (r < 0) + return r; + TAKE_PTR(peer); return 0; } -int config_parse_wireguard_keepalive(const char *unit, - const char *filename, - unsigned line, - const char *section, - unsigned section_line, - const char *lvalue, - int ltype, - const char *rvalue, - void *data, - void *userdata) { - int r; +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) { + + _cleanup_(wireguard_peer_free_or_set_invalidp) WireguardPeer *peer = NULL; uint16_t keepalive = 0; Wireguard *w; - WireguardPeer *peer; + int r; assert(rvalue); assert(data); w = WIREGUARD(data); - assert(w); - peer = wireguard_peer_new(w, section_line); - if (!peer) - return log_oom(); + r = wireguard_peer_new_static(w, filename, section_line, &peer); + if (r < 0) + return r; if (streq(rvalue, "off")) keepalive = 0; else { r = safe_atou16(rvalue, &keepalive); - if (r < 0) - log_syntax(unit, LOG_ERR, filename, line, r, "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "The persistent keepalive interval must be 0-65535. Ignore assignment: %s", + rvalue); + return 0; + } } peer->persistent_keepalive_interval = keepalive; + + TAKE_PTR(peer); return 0; } @@ -730,9 +870,7 @@ static void wireguard_init(NetDev *netdev) { Wireguard *w; assert(netdev); - w = WIREGUARD(netdev); - assert(w); w->flags = WGDEVICE_F_REPLACE_PEERS; @@ -740,32 +878,96 @@ static void wireguard_init(NetDev *netdev) { static void wireguard_done(NetDev *netdev) { Wireguard *w; - WireguardPeer *peer; - WireguardIPmask *mask; - WireguardEndpoint *e; assert(netdev); w = WIREGUARD(netdev); assert(w); - while ((peer = w->peers)) { - LIST_REMOVE(peers, w->peers, peer); - while ((mask = peer->ipmasks)) { - LIST_REMOVE(ipmasks, peer->ipmasks, mask); - free(mask); - } - free(peer); + sd_event_source_unref(w->resolve_retry_event_source); + + explicit_bzero_safe(w->private_key, WG_KEY_LEN); + free(w->private_key_file); + + hashmap_free_with_destructor(w->peers_by_section, wireguard_peer_free); + set_free(w->peers_with_unresolved_endpoint); + set_free(w->peers_with_failed_endpoint); +} + +static int wireguard_read_key_file(const char *filename, uint8_t dest[static WG_KEY_LEN]) { + _cleanup_free_ char *key = NULL; + size_t key_len; + int r; + + if (!filename) + return 0; + + r = read_full_file_full(filename, READ_FULL_FILE_SECURE | READ_FULL_FILE_UNBASE64, &key, &key_len); + if (r < 0) + return r; + + if (key_len != WG_KEY_LEN) { + r = -EINVAL; + goto finalize; } - while ((e = w->unresolved_endpoints)) { - LIST_REMOVE(endpoints, w->unresolved_endpoints, e); - wireguard_endpoint_free(e); - } + memcpy(dest, key, WG_KEY_LEN); + r = 0; - while ((e = w->failed_endpoints)) { - LIST_REMOVE(endpoints, w->failed_endpoints, e); - wireguard_endpoint_free(e); - } +finalize: + explicit_bzero_safe(key, key_len); + return r; +} + +static int wireguard_peer_verify(WireguardPeer *peer) { + NetDev *netdev = NETDEV(peer->wireguard); + int r; + + if (section_is_invalid(peer->section)) + return -EINVAL; + + if (eqzero(peer->public_key)) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: WireGuardPeer section without PublicKey= configured. " + "Ignoring [WireGuardPeer] section from line %u.", + peer->section->filename, peer->section->line); + + r = wireguard_read_key_file(peer->preshared_key_file, peer->preshared_key); + if (r < 0) + return log_netdev_error_errno(netdev, r, + "%s: Failed to read preshared key from '%s'. " + "Ignoring [WireGuardPeer] section from line %u.", + peer->section->filename, peer->preshared_key_file, + peer->section->line); + + return 0; +} + +static int wireguard_verify(NetDev *netdev, const char *filename) { + WireguardPeer *peer, *peer_next; + Wireguard *w; + int r; + + assert(netdev); + w = WIREGUARD(netdev); + assert(w); + + r = wireguard_read_key_file(w->private_key_file, w->private_key); + if (r < 0) + return log_netdev_error_errno(netdev, r, + "Failed to read private key from %s. Dropping network device %s.", + w->private_key_file, netdev->ifname); + + if (eqzero(w->private_key)) + return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL), + "%s: Missing PrivateKey= or PrivateKeyFile=, " + "Dropping network device %s.", + filename, netdev->ifname); + + LIST_FOREACH_SAFE(peers, peer, peer_next, w->peers) + if (wireguard_peer_verify(peer) < 0) + wireguard_peer_free(peer); + + return 0; } const NetDevVTable wireguard_vtable = { @@ -775,4 +977,5 @@ const NetDevVTable wireguard_vtable = { .init = wireguard_init, .done = wireguard_done, .create_type = NETDEV_CREATE_INDEPENDENT, + .config_verify = wireguard_verify, }; diff --git a/src/network/netdev/wireguard.h b/src/network/netdev/wireguard.h index 143e93cdc..4ae520c52 100644 --- a/src/network/netdev/wireguard.h +++ b/src/network/netdev/wireguard.h @@ -7,10 +7,6 @@ typedef struct Wireguard Wireguard; #include "socket-util.h" #include "wireguard-netlink.h" -#ifndef IFNAMSIZ -#define IFNAMSIZ 16 -#endif - typedef struct WireguardIPmask { uint16_t family; union in_addr_union ip; @@ -20,44 +16,41 @@ typedef struct WireguardIPmask { } WireguardIPmask; typedef struct WireguardPeer { + Wireguard *wireguard; + NetworkConfigSection *section; + uint8_t public_key[WG_KEY_LEN]; uint8_t preshared_key[WG_KEY_LEN]; + char *preshared_key_file; uint32_t flags; + uint16_t persistent_keepalive_interval; union sockaddr_union endpoint; - - uint16_t persistent_keepalive_interval; + char *endpoint_host; + char *endpoint_port; LIST_HEAD(WireguardIPmask, ipmasks); LIST_FIELDS(struct WireguardPeer, peers); } WireguardPeer; -typedef struct WireguardEndpoint { - char *host; - char *port; - - NetDev *netdev; - WireguardPeer *peer; - - LIST_FIELDS(struct WireguardEndpoint, endpoints); -} WireguardEndpoint; - struct Wireguard { NetDev meta; unsigned last_peer_section; uint32_t flags; - uint8_t private_key[WG_KEY_LEN]; + char *private_key_file; + uint16_t port; uint32_t fwmark; - uint16_t port; + Hashmap *peers_by_section; + Set *peers_with_unresolved_endpoint; + Set *peers_with_failed_endpoint; LIST_HEAD(WireguardPeer, peers); - LIST_HEAD(WireguardEndpoint, unresolved_endpoints); - LIST_HEAD(WireguardEndpoint, failed_endpoints); unsigned n_retries; + sd_event_source *resolve_retry_event_source; }; DEFINE_NETDEV_CAST(WIREGUARD, Wireguard); @@ -69,5 +62,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_listen_port); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_public_key); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_private_key_file); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key); +CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_preshared_key_file); CONFIG_PARSER_PROTOTYPE(config_parse_wireguard_keepalive); diff --git a/src/network/networkctl.c b/src/network/networkctl.c index b14b81cc8..3881aaa42 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include #include "sd-device.h" #include "sd-hwdb.h" @@ -26,6 +29,7 @@ #include "parse-util.h" #include "pretty-print.h" #include "socket-util.h" +#include "sort-util.h" #include "sparse-endian.h" #include "stdio-util.h" #include "string-table.h" @@ -33,7 +37,6 @@ #include "strv.h" #include "strxcpyx.h" #include "terminal-util.h" -#include "util.h" #include "verbs.h" static PagerFlags arg_pager_flags = 0; @@ -65,7 +68,7 @@ static void operational_state_to_color(const char *state, const char **on, const assert(on); assert(off); - if (streq_ptr(state, "routable")) { + if (STRPTR_IN_SET(state, "routable", "enslaved")) { *on = ansi_highlight_green(); *off = ansi_normal(); } else if (streq_ptr(state, "degraded")) { @@ -107,10 +110,10 @@ 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) { +static int decode_link(sd_netlink_message *m, LinkInfo *info, char **patterns) { const char *name; uint16_t type; - int r; + int ifindex, r; assert(m); assert(info); @@ -122,7 +125,7 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info) { if (type != RTM_NEWLINK) return 0; - r = sd_rtnl_message_link_get_ifindex(m, &info->ifindex); + r = sd_rtnl_message_link_get_ifindex(m, &ifindex); if (r < 0) return r; @@ -130,11 +133,21 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info) { if (r < 0) return r; + if (patterns) { + char str[DECIMAL_STR_MAX(int)]; + + xsprintf(str, "%i", ifindex); + + if (!strv_fnmatch(patterns, str, 0) && !strv_fnmatch(patterns, name, 0)) + return 0; + } + r = sd_rtnl_message_link_get_type(m, &info->iftype); if (r < 0) return r; strscpy(info->name, sizeof info->name, name); + info->ifindex = ifindex; info->has_mac_address = sd_netlink_message_read_ether_addr(m, IFLA_ADDRESS, &info->mac_address) >= 0 && @@ -147,54 +160,7 @@ static int decode_link(sd_netlink_message *m, LinkInfo *info) { return 1; } -static int acquire_link_info_strv(sd_netlink *rtnl, char **l, LinkInfo **ret) { - _cleanup_free_ LinkInfo *links = NULL; - char **i; - size_t c = 0; - int r; - - assert(rtnl); - assert(ret); - - links = new(LinkInfo, strv_length(l)); - if (!links) - return log_oom(); - - STRV_FOREACH(i, l) { - _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; - int ifindex; - - if (parse_ifindex(*i, &ifindex) >= 0) - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, ifindex); - else { - r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0); - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_message_append_string(req, IFLA_IFNAME, *i); - } - if (r < 0) - return rtnl_log_create_error(r); - - r = sd_netlink_call(rtnl, req, 0, &reply); - if (r < 0) - return log_error_errno(r, "Failed to request link: %m"); - - r = decode_link(reply, links + c); - if (r < 0) - return r; - if (r > 0) - c++; - } - - typesafe_qsort(links, c, link_info_compare); - - *ret = TAKE_PTR(links); - - return (int) c; -} - -static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) { +static int acquire_link_info(sd_netlink *rtnl, char **patterns, LinkInfo **ret) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; _cleanup_free_ LinkInfo *links = NULL; size_t allocated = 0, c = 0; @@ -220,7 +186,7 @@ static int acquire_link_info_all(sd_netlink *rtnl, LinkInfo **ret) { if (!GREEDY_REALLOC(links, allocated, c+1)) return -ENOMEM; - r = decode_link(i, links + c); + r = decode_link(i, links + c, patterns); if (r < 0) return r; if (r > 0) @@ -243,17 +209,14 @@ static int list_links(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); - if (argc > 1) - c = acquire_link_info_strv(rtnl, argv + 1, &links); - else - c = acquire_link_info_all(rtnl, &links); + c = acquire_link_info(rtnl, argc > 1 ? argv + 1 : NULL, &links); if (c < 0) return c; (void) pager_open(arg_pager_flags); if (arg_legend) - printf("%3s %-16s %-18s %-11s %-10s\n", + printf("%3s %-16s %-18s %-16s %-10s\n", "IDX", "LINK", "TYPE", @@ -281,7 +244,7 @@ static int list_links(int argc, char *argv[], void *userdata) { t = link_get_type_string(links[i].iftype, d); - printf("%3i %-16s %-18s %s%-11s%s %s%-10s%s\n", + printf("%3i %-16s %-18s %s%-16s%s %s%-10s%s\n", links[i].ifindex, links[i].name, strna(t), on_color_operational, strna(operational_state), off_color_operational, on_color_setup, strna(setup_state), off_color_setup); @@ -887,11 +850,11 @@ static int link_status(int argc, char *argv[], void *userdata) { log_debug_errno(r, "Failed to open hardware database: %m"); if (arg_all) - c = acquire_link_info_all(rtnl, &links); + c = acquire_link_info(rtnl, NULL, &links); else if (argc <= 1) return system_status(rtnl, hwdb); else - c = acquire_link_info_strv(rtnl, argv + 1, &links); + c = acquire_link_info(rtnl, argv + 1, &links); if (c < 0) return c; @@ -965,10 +928,7 @@ static int link_lldp_status(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to connect to netlink: %m"); - if (argc > 1) - c = acquire_link_info_strv(rtnl, argv + 1, &links); - else - c = acquire_link_info_all(rtnl, &links); + c = acquire_link_info(rtnl, argc > 1 ? argv + 1 : NULL, &links); if (c < 0) return c; @@ -1078,9 +1038,9 @@ static int help(void) { " --no-legend Do not show the headers and footers\n" " -a --all Show status for all links\n\n" "Commands:\n" - " list [LINK...] List links\n" - " status [LINK...] Show link status\n" - " lldp [LINK...] Show LLDP neighbors\n" + " list [PATTERN...] List links\n" + " status [PATTERN...] Show link status\n" + " lldp [PATTERN...] Show LLDP neighbors\n" " label Show current address label entries in the kernel\n" "\nSee the %s for details.\n" , program_invocation_short_name diff --git a/src/network/networkd-address-label.c b/src/network/networkd-address-label.c index 94a12f8bf..ab738448c 100644 --- a/src/network/networkd-address-label.c +++ b/src/network/networkd-address-label.c @@ -159,7 +159,7 @@ int config_parse_address_label_prefix(const char *unit, void *data, void *userdata) { - _cleanup_(address_label_freep) AddressLabel *n = NULL; + _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL; Network *network = userdata; int r; @@ -196,7 +196,7 @@ int config_parse_address_label( void *data, void *userdata) { - _cleanup_(address_label_freep) AddressLabel *n = NULL; + _cleanup_(address_label_free_or_set_invalidp) AddressLabel *n = NULL; Network *network = userdata; uint32_t k; int r; diff --git a/src/network/networkd-address-label.h b/src/network/networkd-address-label.h index 6922cb0fa..595072a17 100644 --- a/src/network/networkd-address-label.h +++ b/src/network/networkd-address-label.h @@ -11,6 +11,7 @@ typedef struct AddressLabel AddressLabel; #include "networkd-link.h" #include "networkd-network.h" +#include "networkd-util.h" typedef struct Network Network; typedef struct Link Link; @@ -30,7 +31,7 @@ struct AddressLabel { void address_label_free(AddressLabel *label); -DEFINE_TRIVIAL_CLEANUP_FUNC(AddressLabel*, address_label_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(AddressLabel, address_label_free); int address_label_configure(AddressLabel *address, Link *link, link_netlink_message_handler_t callback, bool update); diff --git a/src/network/networkd-address-pool.c b/src/network/networkd-address-pool.c index 165051506..eaf056d11 100644 --- a/src/network/networkd-address-pool.c +++ b/src/network/networkd-address-pool.c @@ -6,7 +6,9 @@ #include "set.h" #include "string-util.h" -int address_pool_new( +#define RANDOM_PREFIX_TRIAL_MAX 1024 + +static int address_pool_new( Manager *m, AddressPool **ret, int family, @@ -121,35 +123,34 @@ static bool address_pool_prefix_is_taken( int address_pool_acquire(AddressPool *p, unsigned prefixlen, union in_addr_union *found) { union in_addr_union u; + unsigned i; + int r; assert(p); assert(prefixlen > 0); assert(found); - if (p->prefixlen > prefixlen) + if (p->prefixlen >= prefixlen) return 0; u = p->in_addr; - for (;;) { + + for (i = 0; i < RANDOM_PREFIX_TRIAL_MAX; i++) { + r = in_addr_random_prefix(p->family, &u, p->prefixlen, prefixlen); + if (r <= 0) + return r; + if (!address_pool_prefix_is_taken(p, &u, prefixlen)) { - _cleanup_free_ char *s = NULL; - int r; + if (DEBUG_LOGGING) { + _cleanup_free_ char *s = NULL; - r = in_addr_to_string(p->family, &u, &s); - if (r < 0) - return r; - - log_debug("Found range %s/%u", strna(s), prefixlen); + (void) in_addr_to_string(p->family, &u, &s); + log_debug("Found range %s/%u", strna(s), prefixlen); + } *found = u; return 1; } - - if (!in_addr_prefix_next(p->family, &u, prefixlen)) - return 0; - - if (!in_addr_prefix_intersect(p->family, &p->in_addr, p->prefixlen, &u, prefixlen)) - return 0; } return 0; diff --git a/src/network/networkd-address-pool.h b/src/network/networkd-address-pool.h index bd479a517..7db1c4f26 100644 --- a/src/network/networkd-address-pool.h +++ b/src/network/networkd-address-pool.h @@ -19,7 +19,6 @@ struct AddressPool { LIST_FIELDS(AddressPool, address_pools); }; -int address_pool_new(Manager *m, AddressPool **ret, int family, const union in_addr_union *u, unsigned prefixlen); int address_pool_new_from_string(Manager *m, AddressPool **ret, int family, const char *p, unsigned prefixlen); void address_pool_free(AddressPool *p); diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c index 3cdbd9e37..42d61cc0e 100644 --- a/src/network/networkd-address.c +++ b/src/network/networkd-address.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "conf-parser.h" #include "firewall-util.h" +#include "memory-util.h" #include "missing_network.h" #include "netlink-util.h" #include "networkd-address.h" @@ -15,7 +16,6 @@ #include "string-util.h" #include "strv.h" #include "utf8.h" -#include "util.h" #define ADDRESSES_PER_LINK_MAX 2048U #define STATIC_ADDRESSES_PER_NETWORK_MAX 1024U @@ -39,7 +39,7 @@ int address_new(Address **ret) { return 0; } -int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) { +static int address_new_static(Network *network, const char *filename, unsigned section_line, Address **ret) { _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(address_freep) Address *address = NULL; int r; @@ -223,7 +223,7 @@ static int address_establish(Address *address, Link *link) { r = fw_add_masquerade(masq, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); if (r < 0) - log_link_warning_errno(link, r, "Could not enable IP masquerading: %m"); + return r; address->ip_masquerade_done = masq; } @@ -321,7 +321,7 @@ static int address_release(Address *address) { r = fw_add_masquerade(false, AF_INET, 0, &masked, address->prefixlen, NULL, NULL, 0); if (r < 0) - log_link_warning_errno(address->link, r, "Failed to disable IP masquerading: %m"); + return r; address->ip_masquerade_done = false; } @@ -351,7 +351,7 @@ int address_update( address->scope = scope; address->cinfo = *cinfo; - link_update_operstate(address->link); + link_update_operstate(address->link, true); link_check_ready(address->link); if (!ready && @@ -371,16 +371,20 @@ int address_update( int address_drop(Address *address) { Link *link; bool ready; + int r; assert(address); ready = address_is_ready(address); link = address->link; - address_release(address); + r = address_release(address); + if (r < 0) + log_link_warning_errno(link, r, "Failed to disable IP masquerading, ignoring: %m"); + address_free(address); - link_update_operstate(link); + link_update_operstate(link, true); if (link && !ready) link_check_ready(link); @@ -445,7 +449,6 @@ int address_remove( 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); @@ -456,31 +459,30 @@ int address_remove( 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); + _cleanup_free_ char *b = NULL; + + (void) in_addr_to_string(address->family, &address->in_addr, &b); + log_link_debug(link, "Removing address %s", strna(b)); } r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_DELADDR, link->ifindex, address->family); if (r < 0) - return log_error_errno(r, "Could not allocate RTM_DELADDR message: %m"); + return log_link_error_errno(link, r, "Could not allocate RTM_DELADDR message: %m"); r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); if (r < 0) - return log_error_errno(r, "Could not set prefixlen: %m"); + return log_link_error_errno(link, r, "Could not set prefixlen: %m"); - if (address->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); - else if (address->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); + r = netlink_message_append_in_addr_union(req, IFA_LOCAL, address->family, &address->in_addr); if (r < 0) - return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m"); + return log_link_error_errno(link, r, "Could not append IFA_LOCAL attribute: %m"); 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"); + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -498,18 +500,17 @@ static int address_acquire(Link *link, Address *original, Address **ret) { assert(ret); /* Something useful was configured? just use it */ - if (in_addr_is_null(original->family, &original->in_addr) <= 0) - return 0; + r = in_addr_is_null(original->family, &original->in_addr); + if (r <= 0) + return r; /* The address is configured to be 0.0.0.0 or [::] by the user? * Then let's acquire something more useful from the pool. */ r = manager_address_pool_acquire(link->manager, original->family, original->prefixlen, &in_addr); if (r < 0) - return log_link_error_errno(link, r, "Failed to acquire address from pool: %m"); - if (r == 0) { - log_link_error(link, "Couldn't find free address for interface, all taken."); + return r; + if (r == 0) return -EBUSY; - } if (original->family == AF_INET) { /* Pick first address in range for ourselves ... */ @@ -568,11 +569,12 @@ int address_configure( /* 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 && set_size(link->addresses) >= ADDRESSES_PER_LINK_MAX) - return -E2BIG; + return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG), + "Too many addresses are configured, refusing: %m"); r = address_acquire(link, address, &address); if (r < 0) - return r; + return log_link_error_errno(link, r, "Failed to acquire an address from pool: %m"); if (update) r = sd_rtnl_message_new_addr_update(link->manager->rtnl, &req, @@ -581,11 +583,11 @@ int address_configure( r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_NEWADDR, link->ifindex, address->family); if (r < 0) - return log_error_errno(r, "Could not allocate RTM_NEWADDR message: %m"); + return log_link_error_errno(link, r, "Could not allocate RTM_NEWADDR message: %m"); r = sd_rtnl_message_addr_set_prefixlen(req, address->prefixlen); if (r < 0) - return log_error_errno(r, "Could not set prefixlen: %m"); + return log_link_error_errno(link, r, "Could not set prefixlen: %m"); address->flags |= IFA_F_PERMANENT; @@ -606,56 +608,50 @@ int address_configure( r = sd_rtnl_message_addr_set_flags(req, (address->flags & 0xff)); if (r < 0) - return log_error_errno(r, "Could not set flags: %m"); + return log_link_error_errno(link, r, "Could not set flags: %m"); if (address->flags & ~0xff) { r = sd_netlink_message_append_u32(req, IFA_FLAGS, address->flags); if (r < 0) - return log_error_errno(r, "Could not set extended flags: %m"); + return log_link_error_errno(link, r, "Could not set extended flags: %m"); } r = sd_rtnl_message_addr_set_scope(req, address->scope); if (r < 0) - return log_error_errno(r, "Could not set scope: %m"); + return log_link_error_errno(link, r, "Could not set scope: %m"); - if (address->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &address->in_addr.in); - else if (address->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &address->in_addr.in6); + r = netlink_message_append_in_addr_union(req, IFA_LOCAL, address->family, &address->in_addr); if (r < 0) - return log_error_errno(r, "Could not append IFA_LOCAL attribute: %m"); + return log_link_error_errno(link, r, "Could not append IFA_LOCAL attribute: %m"); - if (!in_addr_is_null(address->family, &address->in_addr_peer)) { - if (address->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, IFA_ADDRESS, &address->in_addr_peer.in); - else if (address->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, IFA_ADDRESS, &address->in_addr_peer.in6); + if (in_addr_is_null(address->family, &address->in_addr_peer) == 0) { + r = netlink_message_append_in_addr_union(req, IFA_ADDRESS, address->family, &address->in_addr_peer); if (r < 0) - return log_error_errno(r, "Could not append IFA_ADDRESS attribute: %m"); + return log_link_error_errno(link, r, "Could not append IFA_ADDRESS attribute: %m"); } else if (address->family == AF_INET && address->prefixlen <= 30) { r = sd_netlink_message_append_in_addr(req, IFA_BROADCAST, &address->broadcast); if (r < 0) - return log_error_errno(r, "Could not append IFA_BROADCAST attribute: %m"); + return log_link_error_errno(link, r, "Could not append IFA_BROADCAST attribute: %m"); } if (address->label) { r = sd_netlink_message_append_string(req, IFA_LABEL, address->label); if (r < 0) - return log_error_errno(r, "Could not append IFA_LABEL attribute: %m"); + return log_link_error_errno(link, r, "Could not append IFA_LABEL attribute: %m"); } r = sd_netlink_message_append_cache_info(req, IFA_CACHEINFO, &address->cinfo); if (r < 0) - return log_error_errno(r, "Could not append IFA_CACHEINFO attribute: %m"); + return log_link_error_errno(link, r, "Could not append IFA_CACHEINFO attribute: %m"); r = address_establish(address, link); if (r < 0) - return r; + log_link_warning_errno(link, r, "Could not enable IP masquerading, ignoring: %m"); 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"); + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); } link_ref(link); @@ -666,7 +662,7 @@ int address_configure( r = address_add(link, address->family, &address->in_addr, address->prefixlen, NULL); if (r < 0) { address_release(address); - return log_error_errno(r, "Could not add address: %m"); + return log_link_error_errno(link, r, "Could not add address: %m"); } return 0; @@ -685,7 +681,7 @@ int config_parse_broadcast( void *userdata) { Network *network = userdata; - _cleanup_(address_freep) Address *n = NULL; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -699,13 +695,15 @@ int config_parse_broadcast( return r; if (n->family == AF_INET6) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Broadcast is not valid for IPv6 addresses, ignoring assignment: %s", rvalue); return 0; } r = in_addr_from_string(AF_INET, rvalue, (union in_addr_union*) &n->broadcast); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Broadcast is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Broadcast is invalid, ignoring assignment: %s", rvalue); return 0; } @@ -727,7 +725,7 @@ int config_parse_address(const char *unit, void *userdata) { Network *network = userdata; - _cleanup_(address_freep) Address *n = NULL; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; union in_addr_union buffer; unsigned char prefixlen; int r, f; @@ -768,6 +766,19 @@ int config_parse_address(const char *unit, return 0; } + if (in_addr_is_null(f, &buffer)) { + /* Will use address from address pool. Note that for ipv6 case, prefix of the address + * pool is 8, but 40 bit is used by the global ID and 16 bit by the subnet ID. So, + * let's limit the prefix length to 64 or larger. See RFC4193. */ + if ((f == AF_INET && prefixlen < 8) || + (f == AF_INET6 && prefixlen < 64)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Null address with invalid prefixlen='%u', ignoring assignment: %s", + prefixlen, rvalue); + return 0; + } + } + n->family = f; n->prefixlen = prefixlen; @@ -796,7 +807,7 @@ int config_parse_label( void *data, void *userdata) { - _cleanup_(address_freep) Address *n = NULL; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; Network *network = userdata; int r; @@ -811,7 +822,8 @@ int config_parse_label( return r; if (!address_label_valid(rvalue)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Interface label is too long or invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Interface label is too long or invalid, ignoring assignment: %s", rvalue); return 0; } @@ -820,7 +832,6 @@ int config_parse_label( return log_oom(); n = NULL; - return 0; } @@ -835,7 +846,7 @@ int config_parse_lifetime(const char *unit, void *data, void *userdata) { Network *network = userdata; - _cleanup_(address_freep) Address *n = NULL; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; unsigned k; int r; @@ -849,26 +860,20 @@ int config_parse_lifetime(const char *unit, if (r < 0) return r; - if (STR_IN_SET(rvalue, "forever", "infinity")) { - n->cinfo.ifa_prefered = CACHE_INFO_INFINITY_LIFE_TIME; - n = NULL; - - return 0; - } - - r = safe_atou(rvalue, &k); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse PreferredLifetime, ignoring: %s", rvalue); - return 0; - } - - if (k != 0) - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid PreferredLifetime value, ignoring: %d", k); + /* We accept only "forever", "infinity", or "0". */ + if (STR_IN_SET(rvalue, "forever", "infinity")) + k = CACHE_INFO_INFINITY_LIFE_TIME; + else if (streq(rvalue, "0")) + k = 0; else { - n->cinfo.ifa_prefered = k; - n = NULL; + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid PreferredLifetime= value, ignoring: %s", rvalue); + return 0; } + n->cinfo.ifa_prefered = k; + n = NULL; + return 0; } @@ -883,7 +888,7 @@ int config_parse_address_flags(const char *unit, void *data, void *userdata) { Network *network = userdata; - _cleanup_(address_freep) Address *n = NULL; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -898,7 +903,8 @@ int config_parse_address_flags(const char *unit, r = parse_boolean(rvalue); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse address flag, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse address flag, ignoring: %s", rvalue); return 0; } @@ -912,7 +918,10 @@ int config_parse_address_flags(const char *unit, n->prefix_route = r; else if (streq(lvalue, "AutoJoin")) n->autojoin = r; + else + assert_not_reached("Invalid address flag type."); + n = NULL; return 0; } @@ -927,7 +936,7 @@ int config_parse_address_scope(const char *unit, void *data, void *userdata) { Network *network = userdata; - _cleanup_(address_freep) Address *n = NULL; + _cleanup_(address_free_or_set_invalidp) Address *n = NULL; int r; assert(filename); @@ -949,13 +958,13 @@ int config_parse_address_scope(const char *unit, else { r = safe_atou8(rvalue , &n->scope); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse address scope \"%s\", ignoring assignment: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse address scope \"%s\", ignoring assignment: %m", rvalue); return 0; } } n = NULL; - return 0; } @@ -967,3 +976,19 @@ bool address_is_ready(const Address *a) { else return !(a->flags & (IFA_F_TENTATIVE | IFA_F_DEPRECATED)); } + +int address_section_verify(Address *address) { + if (section_is_invalid(address->section)) + return -EINVAL; + + if (address->family == AF_UNSPEC) { + assert(address->section); + + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: Address section without Address= field configured. " + "Ignoring [Address] section from line %u.", + address->section->filename, address->section->line); + } + + return 0; +} diff --git a/src/network/networkd-address.h b/src/network/networkd-address.h index 4714c07d4..455985d22 100644 --- a/src/network/networkd-address.h +++ b/src/network/networkd-address.h @@ -11,6 +11,7 @@ typedef struct Address Address; #include "networkd-link.h" #include "networkd-network.h" +#include "networkd-util.h" #define CACHE_INFO_INFINITY_LIFE_TIME 0xFFFFFFFFU @@ -46,7 +47,6 @@ struct Address { LIST_FIELDS(Address, addresses); }; -int address_new_static(Network *network, const char *filename, unsigned section, Address **ret); int address_new(Address **ret); void address_free(Address *address); int address_add_foreign(Link *link, int family, const union in_addr_union *in_addr, unsigned char prefixlen, Address **ret); @@ -58,8 +58,9 @@ int address_configure(Address *address, Link *link, link_netlink_message_handler int address_remove(Address *address, Link *link, link_netlink_message_handler_t callback); bool address_equal(Address *a1, Address *a2); bool address_is_ready(const Address *a); +int address_section_verify(Address *a); -DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free); CONFIG_PARSER_PROTOTYPE(config_parse_address); CONFIG_PARSER_PROTOTYPE(config_parse_broadcast); diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index d8ac4552f..85685d6df 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -6,7 +6,6 @@ #include "alloc-util.h" #include "hostname-util.h" #include "parse-util.h" -#include "netdev/vrf.h" #include "network-internal.h" #include "networkd-link.h" #include "networkd-manager.h" @@ -52,7 +51,8 @@ static int route_scope_from_address(const Route *route, const struct in_addr *se static int link_set_dhcp_routes(Link *link) { _cleanup_free_ sd_dhcp_route **static_routes = NULL; bool classless_route = false, static_route = false; - struct in_addr gateway, address; + const struct in_addr *router; + struct in_addr address; int r, n, i; uint32_t table; @@ -67,11 +67,7 @@ static int link_set_dhcp_routes(Link *link) { if (!link->network->dhcp_use_routes) return 0; - /* When the interface is part of an VRF use the VRFs routing table, unless - * there is a another table specified. */ - table = link->network->dhcp_route_table; - if (!link->network->dhcp_route_table_set && link->network->vrf != NULL) - table = VRF(link->network->vrf)->table; + table = link_get_dhcp_route_table(link); r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r < 0) @@ -123,26 +119,21 @@ static int link_set_dhcp_routes(Link *link) { link->dhcp4_messages++; } - r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); - if (r == -ENODATA) - log_link_info_errno(link, r, "DHCP: No gateway received from DHCP server: %m"); + r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); + if (IN_SET(r, 0, -ENODATA)) + log_link_info(link, "DHCP: No gateway received from DHCP server."); else if (r < 0) log_link_warning_errno(link, r, "DHCP error: could not get gateway: %m"); + else if (in4_addr_is_null(&router[0])) + log_link_info(link, "DHCP: Received gateway is null."); /* According to RFC 3442: If the DHCP server returns both a Classless Static Routes option and a Router option, the DHCP client MUST ignore the Router option. */ if (classless_route && static_route) log_link_warning(link, "Classless static routes received from DHCP server: ignoring static-route option and router option"); - if (r >= 0 && !classless_route) { - _cleanup_(route_freep) Route *route = NULL; - _cleanup_(route_freep) Route *route_gw = NULL; - - r = route_new(&route); - if (r < 0) - return log_link_error_errno(link, r, "Could not allocate route: %m"); - - route->protocol = RTPROT_DHCP; + if (r > 0 && !classless_route && !in4_addr_is_null(&router[0])) { + _cleanup_(route_freep) Route *route = NULL, *route_gw = NULL; r = route_new(&route_gw); if (r < 0) @@ -152,7 +143,7 @@ static int link_set_dhcp_routes(Link *link) { * route for the gw host so that we can route no matter the * netmask or existing kernel route tables. */ route_gw->family = AF_INET; - route_gw->dst.in = gateway; + route_gw->dst.in = router[0]; route_gw->dst_prefixlen = 32; route_gw->prefsrc.in = address; route_gw->scope = RT_SCOPE_LINK; @@ -166,9 +157,14 @@ static int link_set_dhcp_routes(Link *link) { link->dhcp4_messages++; + r = route_new(&route); + if (r < 0) + return log_link_error_errno(link, r, "Could not allocate route: %m"); + route->family = AF_INET; - route->gw.in = gateway; + route->gw.in = router[0]; route->prefsrc.in = address; + route->protocol = RTPROT_DHCP; route->priority = link->network->dhcp_route_metric; route->table = table; @@ -187,9 +183,9 @@ static int link_set_dhcp_routes(Link *link) { static int dhcp_lease_lost(Link *link) { _cleanup_(address_freep) Address *address = NULL; + const struct in_addr *router; struct in_addr addr; struct in_addr netmask; - struct in_addr gateway; unsigned prefixlen = 0; int r; @@ -222,15 +218,15 @@ static int dhcp_lease_lost(Link *link) { r = address_new(&address); if (r >= 0) { - r = sd_dhcp_lease_get_router(link->dhcp_lease, &gateway); - if (r >= 0) { + r = sd_dhcp_lease_get_router(link->dhcp_lease, &router); + if (r > 0 && !in4_addr_is_null(&router[0])) { _cleanup_(route_freep) Route *route_gw = NULL; _cleanup_(route_freep) Route *route = NULL; r = route_new(&route_gw); if (r >= 0) { route_gw->family = AF_INET; - route_gw->dst.in = gateway; + route_gw->dst.in = router[0]; route_gw->dst_prefixlen = 32; route_gw->scope = RT_SCOPE_LINK; @@ -240,7 +236,7 @@ static int dhcp_lease_lost(Link *link) { r = route_new(&route); if (r >= 0) { route->family = AF_INET; - route->gw.in = gateway; + route->gw.in = router[0]; route_remove(route, link, NULL); } @@ -265,7 +261,7 @@ static int dhcp_lease_lost(Link *link) { r = sd_dhcp_lease_get_mtu(link->dhcp_lease, &mtu); if (r >= 0 && link->original_mtu != mtu) { - r = link_set_mtu(link, link->original_mtu); + r = link_set_mtu(link, link->original_mtu, true); if (r < 0) { log_link_warning(link, "DHCP error: could not reset MTU"); @@ -399,10 +395,10 @@ static int dhcp_lease_renew(sd_dhcp_client *client, Link *link) { } static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { + const struct in_addr *router; sd_dhcp_lease *lease; struct in_addr address; struct in_addr netmask; - struct in_addr gateway; unsigned prefixlen; uint32_t lifetime = CACHE_INFO_INFINITY_LIFE_TIME; int r; @@ -424,20 +420,20 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { prefixlen = in4_addr_netmask_to_prefixlen(&netmask); - r = sd_dhcp_lease_get_router(lease, &gateway); + r = sd_dhcp_lease_get_router(lease, &router); if (r < 0 && r != -ENODATA) return log_link_error_errno(link, r, "DHCP error: Could not get gateway: %m"); - if (r >= 0) + if (r > 0 && !in4_addr_is_null(&router[0])) log_struct(LOG_INFO, LOG_LINK_INTERFACE(link), LOG_LINK_MESSAGE(link, "DHCPv4 address %u.%u.%u.%u/%u via %u.%u.%u.%u", ADDRESS_FMT_VAL(address), prefixlen, - ADDRESS_FMT_VAL(gateway)), + ADDRESS_FMT_VAL(router[0])), "ADDRESS=%u.%u.%u.%u", ADDRESS_FMT_VAL(address), "PREFIXLEN=%u", prefixlen, - "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(gateway)); + "GATEWAY=%u.%u.%u.%u", ADDRESS_FMT_VAL(router[0])); else log_struct(LOG_INFO, LOG_LINK_INTERFACE(link), @@ -455,7 +451,7 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { r = sd_dhcp_lease_get_mtu(lease, &mtu); if (r >= 0) { - r = link_set_mtu(link, mtu); + r = link_set_mtu(link, mtu, true); if (r < 0) log_link_error_errno(link, r, "Failed to set MTU to %" PRIu16 ": %m", mtu); } diff --git a/src/network/networkd-dhcp6.c b/src/network/networkd-dhcp6.c index c1fba03f9..90361c9f4 100644 --- a/src/network/networkd-dhcp6.c +++ b/src/network/networkd-dhcp6.c @@ -142,10 +142,15 @@ int dhcp6_lease_pd_prefix_lost(sd_dhcp6_client *client, Link* link) { 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_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, 0, &route); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to add unreachable route to delete for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + continue; + } + + route_update(route, NULL, 0, NULL, NULL, 0, 0, RTN_UNREACHABLE); r = route_remove(route, link, dhcp6_route_remove_handler); if (r < 0) { @@ -288,7 +293,8 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { } if (pd_prefix_len < 64) { - Route *route = NULL; + _cleanup_(route_freep) Route *route = NULL; + uint32_t table; (void) in_addr_to_string(AF_INET6, &pd_prefix, &buf); @@ -300,22 +306,26 @@ static int dhcp6_lease_pd_prefix_acquired(sd_dhcp6_client *client, Link *link) { 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); + table = link_get_dhcp_route_table(link); + + r = route_add(link, AF_INET6, &pd_prefix, pd_prefix_len, 0, 0, table, &route); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to add unreachable route for DHCPv6 delegated subnet %s/%u: %m", + strnull(buf), + pd_prefix_len); + continue; + } + + 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); diff --git a/src/network/networkd-fdb.c b/src/network/networkd-fdb.c index 324f6a5a1..fa1394953 100644 --- a/src/network/networkd-fdb.c +++ b/src/network/networkd-fdb.c @@ -18,7 +18,7 @@ #define STATIC_FDB_ENTRIES_PER_NETWORK_MAX 1024U /* create a new FDB entry or get an existing one. */ -int fdb_entry_new_static( +static int fdb_entry_new_static( Network *network, const char *filename, unsigned section_line, @@ -189,7 +189,7 @@ int config_parse_fdb_hwaddr( void *userdata) { Network *network = userdata; - _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL; + _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL; int r; assert(filename); @@ -235,7 +235,7 @@ int config_parse_fdb_vlan_id( void *userdata) { Network *network = userdata; - _cleanup_(fdb_entry_freep) FdbEntry *fdb_entry = NULL; + _cleanup_(fdb_entry_free_or_set_invalidp) FdbEntry *fdb_entry = NULL; int r; assert(filename); diff --git a/src/network/networkd-fdb.h b/src/network/networkd-fdb.h index 55fc3a217..6b7da2e74 100644 --- a/src/network/networkd-fdb.h +++ b/src/network/networkd-fdb.h @@ -8,6 +8,7 @@ #include "conf-parser.h" #include "list.h" #include "macro.h" +#include "networkd-util.h" typedef struct Network Network; typedef struct FdbEntry FdbEntry; @@ -24,11 +25,10 @@ struct FdbEntry { LIST_FIELDS(FdbEntry, static_fdb_entries); }; -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); -DEFINE_TRIVIAL_CLEANUP_FUNC(FdbEntry*, fdb_entry_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(FdbEntry, fdb_entry_free); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_hwaddr); CONFIG_PARSER_PROTOTYPE(config_parse_fdb_vlan_id); diff --git a/src/network/networkd-ipv4ll.c b/src/network/networkd-ipv4ll.c index 3562e9053..829dc48a0 100644 --- a/src/network/networkd-ipv4ll.c +++ b/src/network/networkd-ipv4ll.c @@ -65,12 +65,28 @@ static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *l link->ipv4ll_route = true; - if (link->ipv4ll_address == true) - link_check_ready(link); + link_check_ready(link); return 1; } +static int ipv4ll_route_configure(Link *link) { + _cleanup_(route_freep) Route *route = NULL; + int r; + + r = route_new(&route); + if (r < 0) + return r; + + route->family = AF_INET; + route->scope = RT_SCOPE_LINK; + route->protocol = RTPROT_STATIC; + route->priority = IPV4LL_ROUTE_METRIC; + route->table = link_get_vrf_table(link); + + return route_configure(route, link, ipv4ll_route_handler); +} + static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { int r; @@ -86,21 +102,26 @@ static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link link->ipv4ll_address = true; - if (link->ipv4ll_route) - link_check_ready(link); + r = ipv4ll_route_configure(link); + if (r < 0) { + log_link_error_errno(link, r, "Failed to configure ipv4ll route: %m"); + link_enter_failed(link); + } return 1; } static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { _cleanup_(address_freep) Address *ll_addr = NULL; - _cleanup_(route_freep) Route *route = NULL; struct in_addr address; int r; assert(ll); assert(link); + link->ipv4ll_address = false; + link->ipv4ll_route = false; + r = sd_ipv4ll_get_address(ll, &address); if (r == -ENOENT) return 0; @@ -124,23 +145,6 @@ static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { if (r < 0) return r; - link->ipv4ll_address = false; - - r = route_new(&route); - if (r < 0) - return r; - - route->family = AF_INET; - route->scope = RT_SCOPE_LINK; - route->protocol = RTPROT_STATIC; - route->priority = IPV4LL_ROUTE_METRIC; - - r = route_configure(route, link, ipv4ll_route_handler); - if (r < 0) - return r; - - link->ipv4ll_route = false; - return 0; } @@ -176,6 +180,7 @@ static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { case SD_IPV4LL_EVENT_BIND: r = ipv4ll_address_claimed(ll, link); if (r < 0) { + log_link_error(link, "Failed to configure ipv4ll address: %m"); link_enter_failed(link); return; } diff --git a/src/network/networkd-ipv6-proxy-ndp.c b/src/network/networkd-ipv6-proxy-ndp.c index f594b27f7..e2d77e9ad 100644 --- a/src/network/networkd-ipv6-proxy-ndp.c +++ b/src/network/networkd-ipv6-proxy-ndp.c @@ -10,8 +10,9 @@ #include "networkd-link.h" #include "networkd-manager.h" #include "networkd-network.h" -#include "string-util.h" #include "socket-util.h" +#include "string-util.h" +#include "sysctl-util.h" static bool ipv6_proxy_ndp_is_needed(Link *link) { assert(link); @@ -32,8 +33,8 @@ static bool ipv6_proxy_ndp_is_needed(Link *link) { } static int ipv6_proxy_ndp_set(Link *link) { - const char *p = NULL; - int r, v; + bool v; + int r; assert(link); @@ -41,16 +42,15 @@ static int ipv6_proxy_ndp_set(Link *link) { return 0; 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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v); if (r < 0) log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m"); return 0; } -int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) { +static int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) { _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; assert(network); @@ -122,9 +122,8 @@ int config_parse_ipv6_proxy_ndp_address( return 0; } - r = in_addr_is_null(AF_INET6, &buffer); - if (r != 0) { - log_syntax(unit, LOG_ERR, filename, line, r, + if (in_addr_is_null(AF_INET6, &buffer)) { + log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 proxy NDP address cannot be the ANY address, ignoring: %s", rvalue); return 0; } diff --git a/src/network/networkd-ipv6-proxy-ndp.h b/src/network/networkd-ipv6-proxy-ndp.h index 546e8561f..d6666beab 100644 --- a/src/network/networkd-ipv6-proxy-ndp.h +++ b/src/network/networkd-ipv6-proxy-ndp.h @@ -10,13 +10,12 @@ typedef struct IPv6ProxyNDPAddress IPv6ProxyNDPAddress; typedef struct Link Link; struct IPv6ProxyNDPAddress { - Network *network; - struct in6_addr in_addr; + Network *network; + struct in6_addr in_addr; - LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); + LIST_FIELDS(IPv6ProxyNDPAddress, ipv6_proxy_ndp_addresses); }; -int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress ** ipv6_proxy_ndp_address); void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address); int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address); int ipv6_proxy_ndp_addresses_configure(Link *link); diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index 22392d70b..3e334c8d2 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -14,6 +14,7 @@ #include "fd-util.h" #include "fileio.h" #include "missing_network.h" +#include "netdev/vrf.h" #include "netlink-util.h" #include "network-internal.h" #include "networkd-ipv6-proxy-ndp.h" @@ -28,10 +29,30 @@ #include "stdio-util.h" #include "string-table.h" #include "strv.h" +#include "sysctl-util.h" #include "tmpfile-util.h" +#include "udev-util.h" #include "util.h" #include "virt.h" +uint32_t link_get_vrf_table(Link *link) { + return link->network->vrf ? VRF(link->network->vrf)->table : RT_TABLE_MAIN; +} + +uint32_t link_get_dhcp_route_table(Link *link) { + /* When the interface is part of an VRF use the VRFs routing table, unless + * another table is explicitly specified. */ + if (link->network->dhcp_route_table_set) + return link->network->dhcp_route_table; + return link_get_vrf_table(link); +} + +uint32_t link_get_ipv6_accept_ra_route_table(Link *link) { + if (link->network->ipv6_accept_ra_route_table_set) + return link->network->ipv6_accept_ra_route_table; + return link_get_vrf_table(link); +} + DUID* link_get_duid(Link *link) { if (link->network->duid.type != _DUID_TYPE_INVALID) return &link->network->duid; @@ -51,6 +72,12 @@ static bool link_dhcp6_enabled(Link *link) { if (!link->network) return false; + if (link->network->bond) + return false; + + if (manager_sysctl_ipv6_enabled(link->manager) == 0) + return false; + return link->network->dhcp & ADDRESS_FAMILY_IPV6; } @@ -63,6 +90,9 @@ static bool link_dhcp4_enabled(Link *link) { if (!link->network) return false; + if (link->network->bond) + return false; + return link->network->dhcp & ADDRESS_FAMILY_IPV4; } @@ -75,6 +105,9 @@ static bool link_dhcp4_server_enabled(Link *link) { if (!link->network) return false; + if (link->network->bond) + return false; + return link->network->dhcp_server; } @@ -87,7 +120,10 @@ static bool link_ipv4ll_enabled(Link *link) { if (!link->network) return false; - if (streq_ptr(link->kind, "wireguard")) + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard")) + return false; + + if (link->network->bond) return false; return link->network->link_local & ADDRESS_FAMILY_IPV4; @@ -105,7 +141,13 @@ static bool link_ipv6ll_enabled(Link *link) { if (!link->network) return false; - if (streq_ptr(link->kind, "wireguard")) + if (STRPTR_IN_SET(link->kind, "vrf", "wireguard")) + return false; + + if (link->network->bond) + return false; + + if (manager_sysctl_ipv6_enabled(link->manager) == 0) return false; return link->network->link_local & ADDRESS_FAMILY_IPV6; @@ -117,7 +159,10 @@ static bool link_ipv6_enabled(Link *link) { if (!socket_ipv6_is_supported()) return false; - if (link->network->bridge) + if (link->network->bond) + return false; + + if (manager_sysctl_ipv6_enabled(link->manager) == 0) return false; /* DHCPv6 client will not be started if no IPv6 link-local address is configured. */ @@ -199,6 +244,9 @@ static bool link_ipv6_forward_enabled(Link *link) { if (link->network->ip_forward == _ADDRESS_FAMILY_BOOLEAN_INVALID) return false; + if (manager_sysctl_ipv6_enabled(link->manager) == 0) + return false; + return link->network->ip_forward & ADDRESS_FAMILY_IPV6; } @@ -263,7 +311,6 @@ static IPv6PrivacyExtensions link_ipv6_privacy_extensions(Link *link) { } static int link_enable_ipv6(Link *link) { - const char *p = NULL; bool disabled; int r; @@ -272,9 +319,7 @@ static int link_enable_ipv6(Link *link) { disabled = !link_ipv6_enabled(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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "disable_ipv6", disabled); if (r < 0) log_link_warning_errno(link, r, "Cannot %s IPv6 for interface %s: %m", enable_disable(!disabled), link->ifname); @@ -284,8 +329,40 @@ static int link_enable_ipv6(Link *link) { return 0; } -void link_update_operstate(Link *link) { +static bool link_is_enslaved(Link *link) { + if (link->flags & IFF_SLAVE) + /* Even if the link is not managed by networkd, honor IFF_SLAVE flag. */ + return true; + + if (!link->enslaved_raw) + return false; + + if (!link->network) + return false; + + if (link->network->bridge) + /* TODO: support the case when link is not managed by networkd. */ + return true; + + return false; +} + +static void link_update_master_operstate(Link *link, NetDev *netdev) { + Link *master; + + if (!netdev) + return; + + if (link_get(link->manager, netdev->ifindex, &master) < 0) + return; + + link_update_operstate(master, true); +} + +void link_update_operstate(Link *link, bool also_update_master) { LinkOperationalState operstate; + Iterator i; + assert(link); if (link->kernel_operstate == IF_OPER_DORMANT) @@ -293,7 +370,6 @@ void link_update_operstate(Link *link) { else if (link_has_carrier(link)) { Address *address; uint8_t scope = RT_SCOPE_NOWHERE; - Iterator i; /* if we have carrier, check what addresses we have */ SET_FOREACH(address, link->addresses, i) { @@ -327,11 +403,31 @@ void link_update_operstate(Link *link) { else operstate = LINK_OPERSTATE_OFF; + if (IN_SET(operstate, LINK_OPERSTATE_DEGRADED, LINK_OPERSTATE_CARRIER) && + link_is_enslaved(link)) + operstate = LINK_OPERSTATE_ENSLAVED; + + if (operstate >= LINK_OPERSTATE_CARRIER) { + Link *slave; + + HASHMAP_FOREACH(slave, link->slaves, i) { + link_update_operstate(slave, false); + + if (slave->operstate < LINK_OPERSTATE_CARRIER) + operstate = LINK_OPERSTATE_DEGRADED_CARRIER; + } + } + if (link->operstate != operstate) { link->operstate = operstate; link_send_changed(link, "OperationalState", NULL); link_dirty(link); } + + if (also_update_master && link->network) { + link_update_master_operstate(link, link->network->bond); + link_update_master_operstate(link, link->network->bridge); + } } #define FLAG_STRING(string, flag, old, new) \ @@ -406,7 +502,7 @@ static int link_update_flags(Link *link, sd_netlink_message *m) { link->flags = flags; link->kernel_operstate = operstate; - link_update_operstate(link); + link_update_operstate(link, true); return 0; } @@ -520,8 +616,8 @@ static void link_detach_from_manager(Link *link) { } static Link *link_free(Link *link) { + Link *carrier, *master; Address *address; - Link *carrier; Route *route; Iterator i; @@ -587,6 +683,18 @@ static Link *link_free(Link *link) { hashmap_remove(link->bound_by_links, INT_TO_PTR(carrier->ifindex)); hashmap_free(link->bound_by_links); + hashmap_free(link->slaves); + + if (link->network) { + if (link->network->bond && + link_get(link->manager, link->network->bond->ifindex, &master) >= 0) + (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex)); + + if (link->network->bridge && + link_get(link->manager, link->network->bridge->ifindex, &master) >= 0) + (void) hashmap_remove(master->slaves, INT_TO_PTR(link->ifindex)); + } + return mfree(link); } @@ -714,6 +822,35 @@ static Address* link_find_dhcp_server_address(Link *link) { return NULL; } +static int link_join_netdevs_after_configured(Link *link) { + NetDev *netdev; + Iterator i; + int r; + + HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { + if (netdev->ifindex > 0) + /* Assume already enslaved. */ + continue; + + if (netdev_get_create_type(netdev) != NETDEV_CREATE_AFTER_CONFIGURED) + continue; + + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(netdev), + LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname)); + + r = netdev_join(netdev, link, NULL); + if (r < 0) + return log_struct_errno(LOG_WARNING, r, + LOG_LINK_INTERFACE(link), + LOG_NETDEV_INTERFACE(netdev), + LOG_LINK_MESSAGE(link, "Could not join netdev '%s': %m", netdev->ifname)); + } + + return 0; +} + static void link_enter_configured(Link *link) { assert(link); assert(link->network); @@ -725,6 +862,8 @@ static void link_enter_configured(Link *link) { link_set_state(link, LINK_STATE_CONFIGURED); + (void) link_join_netdevs_after_configured(link); + link_dirty(link); } @@ -879,23 +1018,20 @@ void link_check_ready(Link *link) { !link->ipv4ll_route) return; - if (!link->network->bridge) { + if (link_ipv6ll_enabled(link) && + in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) + return; - if (link_ipv6ll_enabled(link)) - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) > 0) - return; + if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && + !link->dhcp4_configured) || + (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && + !link->dhcp6_configured) || + (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) && + !link->dhcp4_configured && !link->dhcp6_configured)) + return; - if ((link_dhcp4_enabled(link) && !link_dhcp6_enabled(link) && - !link->dhcp4_configured) || - (link_dhcp6_enabled(link) && !link_dhcp4_enabled(link) && - !link->dhcp6_configured) || - (link_dhcp4_enabled(link) && link_dhcp6_enabled(link) && - !link->dhcp4_configured && !link->dhcp6_configured)) - return; - - if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) - return; - } + if (link_ipv6_accept_ra_enabled(link) && !link->ndisc_configured) + return; if (link->state != LINK_STATE_CONFIGURED) link_enter_configured(link); @@ -994,7 +1130,7 @@ static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { if (link->network->dhcp_use_dns && link->dhcp_lease) { const struct in_addr *da = NULL; - int n; + int j, n; n = sd_dhcp_lease_get_dns(link->dhcp_lease, &da); if (n > 0) { @@ -1002,8 +1138,9 @@ static int link_push_uplink_dns_to_dhcp_server(Link *link, sd_dhcp_server *s) { if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) return log_oom(); - memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); - n_addresses += n; + for (j = 0; j < n; j++) + if (in4_addr_is_non_local(&da[j])) + addresses[n_addresses++] = da[j]; } } @@ -1042,7 +1179,7 @@ static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { if (link->network->dhcp_use_ntp && link->dhcp_lease) { const struct in_addr *da = NULL; - int n; + int j, n; n = sd_dhcp_lease_get_ntp(link->dhcp_lease, &da); if (n > 0) { @@ -1050,8 +1187,9 @@ static int link_push_uplink_ntp_to_dhcp_server(Link *link, sd_dhcp_server *s) { if (!GREEDY_REALLOC(addresses, n_allocated, n_addresses + n)) return log_oom(); - memcpy(addresses + n_addresses, da, n * sizeof(struct in_addr)); - n_addresses += n; + for (j = 0; j < n; j++) + if (in4_addr_is_non_local(&da[j])) + addresses[n_addresses++] = da[j]; } } @@ -1264,15 +1402,12 @@ static int link_set_bridge_vlan(Link *link) { } static int link_set_proxy_arp(Link *link) { - const char *p = NULL; int r; if (!link_proxy_arp_enabled(link)) return 0; - 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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_boolean(AF_INET, link->ifname, "proxy_arp", link->network->proxy_arp > 0); if (r < 0) log_link_warning_errno(link, r, "Cannot configure proxy ARP for interface: %m"); @@ -1323,7 +1458,7 @@ static int set_mtu_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) return 1; } -int link_set_mtu(Link *link, uint32_t mtu) { +int link_set_mtu(Link *link, uint32_t mtu, bool force) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -1331,7 +1466,10 @@ int link_set_mtu(Link *link, uint32_t mtu) { assert(link->manager); assert(link->manager->rtnl); - if (link->mtu == mtu || link->setting_mtu) + if (mtu == 0 || link->setting_mtu) + return 0; + + if (force ? link->mtu == mtu : link->mtu >= mtu) return 0; log_link_debug(link, "Setting MTU: %" PRIu32, mtu); @@ -1341,7 +1479,7 @@ 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, then disable IPv6 else enable it. */ (void) link_enable_ipv6(link); /* IPv6 protocol requires a minimum MTU of IPV6_MTU_MIN(1280) bytes @@ -1488,12 +1626,30 @@ static int link_set_bridge(Link *link) { return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_UNICAST_FLOOD attribute: %m"); } + if (link->network->multicast_flood >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_MCAST_FLOOD, link->network->multicast_flood); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_MCAST_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->neighbor_suppression >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_NEIGH_SUPPRESS, link->network->neighbor_suppression); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_NEIGH_SUPPRESS attribute: %m"); + } + + if (link->network->learning >= 0) { + r = sd_netlink_message_append_u8(req, IFLA_BRPORT_LEARNING, link->network->learning); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_BRPORT_LEARNING attribute: %m"); + } + if (link->network->cost != 0) { r = sd_netlink_message_append_u32(req, IFLA_BRPORT_COST, link->network->cost); if (r < 0) @@ -1520,7 +1676,26 @@ static int link_set_bridge(Link *link) { return r; } -static int link_bond_set(Link *link) { +static int link_set_bond_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) { + log_link_warning_errno(link, r, "Could not set bonding interface: %m"); + return 1; + } + + return 1; +} + +static int link_set_bond(Link *link) { _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; int r; @@ -1563,7 +1738,7 @@ 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 = netlink_call_async(link->manager->rtnl, NULL, req, set_flags_handler, + r = netlink_call_async(link->manager->rtnl, NULL, req, link_set_bond_handler, link_netlink_destroy_callback, link); if (r < 0) return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); @@ -1573,6 +1748,28 @@ static int link_bond_set(Link *link) { return r; } +static int link_append_to_master(Link *link, NetDev *netdev) { + Link *master; + int r; + + assert(link); + assert(netdev); + + r = link_get(link->manager, netdev->ifindex, &master); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&master->slaves, NULL); + if (r < 0) + return r; + + r = hashmap_put(master->slaves, INT_TO_PTR(link->ifindex), link); + if (r < 0) + return r; + + return 0; +} + static int link_lldp_save(Link *link) { _cleanup_free_ char *temp_path = NULL; _cleanup_fclose_ FILE *f = NULL; @@ -1734,7 +1931,7 @@ static int link_acquire_conf(Link *link) { if (r < 0) return r; - if (in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address) == 0) { + if (!in_addr_is_null(AF_INET6, (const union in_addr_union*) &link->ipv6ll_address)) { r = link_acquire_ipv6_conf(link); if (r < 0) return r; @@ -1804,20 +2001,12 @@ static int link_configure_addrgen_mode(Link *link) { if (!link_ipv6ll_enabled(link)) ipv6ll_mode = IN6_ADDR_GEN_MODE_NONE; - else { - const char *p = NULL; - _cleanup_free_ char *stable_secret = NULL; - - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/stable_secret"); - + else if (sysctl_read_ip_property(AF_INET6, link->ifname, "stable_secret", NULL) < 0) /* The file may not exist. And event if it exists, when stable_secret is unset, - * then reading the file fails and EIO is returned. */ - r = read_one_line_file(p, &stable_secret); - if (r < 0) - ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; - else - ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; - } + * reading the file fails with EIO. */ + ipv6ll_mode = IN6_ADDR_GEN_MODE_EUI64; + else + ipv6ll_mode = IN6_ADDR_GEN_MODE_STABLE_PRIVACY; r = sd_netlink_message_append_u8(req, IFLA_INET6_ADDR_GEN_MODE, ipv6ll_mode); if (r < 0) @@ -2022,6 +2211,19 @@ static int link_set_can(Link *link) { return log_link_error_errno(link, r, "Could not append IFLA_CAN_RESTART_MS attribute: %m"); } + if (link->network->can_triple_sampling >= 0) { + struct can_ctrlmode cm = { + .mask = CAN_CTRLMODE_3_SAMPLES, + .flags = link->network->can_triple_sampling ? CAN_CTRLMODE_3_SAMPLES : 0, + }; + + log_link_debug(link, "%sabling triple-sampling", link->network->can_triple_sampling ? "En" : "Dis"); + + r = sd_netlink_message_append_data(m, IFLA_CAN_CTRLMODE, &cm, sizeof(cm)); + if (r < 0) + return log_link_error_errno(link, r, "Could not append IFLA_CAN_CTRLMODE attribute: %m"); + } + r = sd_netlink_message_close_container(m); if (r < 0) return log_link_error_errno(link, r, "Failed to close netlink container: %m"); @@ -2371,12 +2573,20 @@ static int link_joined(Link *link) { r = link_set_bridge(link); if (r < 0) log_link_error_errno(link, r, "Could not set bridge message: %m"); + + r = link_append_to_master(link, link->network->bridge); + if (r < 0) + log_link_error_errno(link, r, "Failed to add to bridge master's slave list: %m"); } if (link->network->bond) { - r = link_bond_set(link); + r = link_set_bond(link); if (r < 0) log_link_error_errno(link, r, "Could not set bond message: %m"); + + r = link_append_to_master(link, link->network->bond); + if (r < 0) + log_link_error_errno(link, r, "Failed to add to bond master's slave list: %m"); } if (link->network->use_br_vlan && @@ -2400,6 +2610,8 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li assert(link); assert(link->network); + assert(link->enslaving > 0); + assert(!link->enslaved_raw); link->enslaving--; @@ -2414,8 +2626,10 @@ static int netdev_join_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *li } else log_link_debug(link, "Joined netdev"); - if (link->enslaving <= 0) + if (link->enslaving == 0) { + link->enslaved_raw = true; link_joined(link); + } return 1; } @@ -2432,12 +2646,8 @@ static int link_enter_join_netdev(Link *link) { link_set_state(link, LINK_STATE_CONFIGURING); link_dirty(link); - - if (!link->network->bridge && - !link->network->bond && - !link->network->vrf && - hashmap_isempty(link->network->stacked_netdevs)) - return link_joined(link); + link->enslaving = 0; + link->enslaved_raw = false; if (link->network->bond) { if (link->network->bond->state == NETDEV_STATE_READY && @@ -2449,6 +2659,8 @@ static int link_enter_join_netdev(Link *link) { LOG_NETDEV_INTERFACE(link->network->bond), LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bond->ifname)); + link->enslaving++; + r = netdev_join(link->network->bond, link, netdev_join_handler); if (r < 0) { log_struct_errno(LOG_WARNING, r, @@ -2458,8 +2670,6 @@ static int link_enter_join_netdev(Link *link) { link_enter_failed(link); return r; } - - link->enslaving++; } if (link->network->bridge) { @@ -2468,6 +2678,8 @@ static int link_enter_join_netdev(Link *link) { LOG_NETDEV_INTERFACE(link->network->bridge), LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->bridge->ifname)); + link->enslaving++; + r = netdev_join(link->network->bridge, link, netdev_join_handler); if (r < 0) { log_struct_errno(LOG_WARNING, r, @@ -2477,8 +2689,6 @@ static int link_enter_join_netdev(Link *link) { link_enter_failed(link); return r; } - - link->enslaving++; } if (link->network->vrf) { @@ -2487,6 +2697,8 @@ static int link_enter_join_netdev(Link *link) { LOG_NETDEV_INTERFACE(link->network->vrf), LOG_LINK_MESSAGE(link, "Enslaving by '%s'", link->network->vrf->ifname)); + link->enslaving++; + r = netdev_join(link->network->vrf, link, netdev_join_handler); if (r < 0) { log_struct_errno(LOG_WARNING, r, @@ -2496,22 +2708,24 @@ static int link_enter_join_netdev(Link *link) { link_enter_failed(link); return r; } - - link->enslaving++; } HASHMAP_FOREACH(netdev, link->network->stacked_netdevs, i) { - if (netdev->ifindex > 0) { - link_joined(link); + if (netdev->ifindex > 0) + /* Assume already enslaved. */ + continue; + + if (netdev_get_create_type(netdev) != NETDEV_CREATE_STACKED) continue; - } log_struct(LOG_DEBUG, LOG_LINK_INTERFACE(link), LOG_NETDEV_INTERFACE(netdev), LOG_LINK_MESSAGE(link, "Enslaving by '%s'", netdev->ifname)); + link->enslaving++; + r = netdev_join(netdev, link, netdev_join_handler); if (r < 0) { log_struct_errno(LOG_WARNING, r, @@ -2521,10 +2735,11 @@ static int link_enter_join_netdev(Link *link) { link_enter_failed(link); return r; } - - link->enslaving++; } + if (link->enslaving == 0) + return link_joined(link); + return 0; } @@ -2541,7 +2756,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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property(AF_INET, NULL, "ip_forward", "1"); if (r < 0) log_link_warning_errno(link, r, "Cannot turn on IPv4 packet forwarding, ignoring: %m"); @@ -2563,7 +2778,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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property(AF_INET6, "all", "forwarding", "1"); if (r < 0) log_link_warning_errno(link, r, "Cannot configure IPv6 packet forwarding, ignoring: %m"); @@ -2571,19 +2786,14 @@ static int link_set_ipv6_forward(Link *link) { } static int link_set_ipv6_privacy_extensions(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; IPv6PrivacyExtensions s; - const char *p = NULL; int r; s = link_ipv6_privacy_extensions(link); if (s < 0) return 0; - 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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "use_tempaddr", (int) link->network->ipv6_privacy_extensions); if (r < 0) log_link_warning_errno(link, r, "Cannot configure IPv6 privacy extension for interface: %m"); @@ -2591,7 +2801,6 @@ static int link_set_ipv6_privacy_extensions(Link *link) { } static int link_set_ipv6_accept_ra(Link *link) { - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2604,10 +2813,7 @@ static int link_set_ipv6_accept_ra(Link *link) { if (!link->network) return 0; - 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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property(AF_INET6, link->ifname, "accept_ra", "0"); if (r < 0) log_link_warning_errno(link, r, "Cannot disable kernel IPv6 accept_ra for interface: %m"); @@ -2615,8 +2821,6 @@ static int link_set_ipv6_accept_ra(Link *link) { } static int link_set_ipv6_dad_transmits(Link *link) { - char buf[DECIMAL_STR_MAX(int) + 1]; - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2632,10 +2836,7 @@ static int link_set_ipv6_dad_transmits(Link *link) { if (link->network->ipv6_dad_transmits < 0) return 0; - 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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "dad_transmits", link->network->ipv6_dad_transmits); if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 dad transmits for interface: %m"); @@ -2643,8 +2844,6 @@ static int link_set_ipv6_dad_transmits(Link *link) { } static int link_set_ipv6_hop_limit(Link *link) { - char buf[DECIMAL_STR_MAX(int) + 1]; - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2660,10 +2859,7 @@ static int link_set_ipv6_hop_limit(Link *link) { if (link->network->ipv6_hop_limit < 0) return 0; - 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 | WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_int(AF_INET6, link->ifname, "hop_limit", link->network->ipv6_hop_limit); if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 hop limit for interface: %m"); @@ -2671,8 +2867,6 @@ static int link_set_ipv6_hop_limit(Link *link) { } static int link_set_ipv6_mtu(Link *link) { - char buf[DECIMAL_STR_MAX(unsigned) + 1]; - const char *p = NULL; int r; /* Make this a NOP if IPv6 is not available */ @@ -2685,11 +2879,7 @@ static int link_set_ipv6_mtu(Link *link) { if (link->network->ipv6_mtu == 0) return 0; - p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/mtu"); - - xsprintf(buf, "%" PRIu32, link->network->ipv6_mtu); - - r = write_string_file(p, buf, WRITE_STRING_FILE_DISABLE_BUFFER); + r = sysctl_write_ip_property_uint32(AF_INET6, link->ifname, "mtu", link->network->ipv6_mtu); if (r < 0) log_link_warning_errno(link, r, "Cannot set IPv6 MTU for interface: %m"); @@ -2995,11 +3185,9 @@ static int link_configure(Link *link) { return r; } - if (link->network->mtu > 0) { - r = link_set_mtu(link, link->network->mtu); - if (r < 0) - return r; - } + r = link_set_mtu(link, link->network->mtu, link->network->mtu_is_set); + if (r < 0) + return r; if (socket_ipv6_is_supported()) { r = link_configure_addrgen_mode(link); @@ -3500,6 +3688,16 @@ int link_add(Manager *m, sd_netlink_message *message, Link **ret) { return 0; } + r = device_is_renaming(device); + if (r < 0) { + log_link_warning_errno(link, r, "Failed to determine the device is renamed or not: %m"); + goto failed; + } + if (r > 0) { + log_link_debug(link, "Interface is under renaming, pending initialization."); + return 0; + } + r = link_initialized(link, device); if (r < 0) goto failed; @@ -3565,6 +3763,9 @@ static int link_carrier_lost(Link *link) { assert(link); + if (link->network && link->network->ignore_carrier_loss) + return 0; + /* 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. */ @@ -3640,20 +3841,14 @@ int link_update(Link *link, sd_netlink_message *m) { r = sd_netlink_message_read_string(m, IFLA_IFNAME, &ifname); if (r >= 0 && !streq(ifname, link->ifname)) { + Manager *manager = link->manager; + log_link_info(link, "Interface name change detected, %s has been renamed to %s.", link->ifname, ifname); - if (link->state == LINK_STATE_PENDING) { - r = free_and_strdup(&link->ifname, ifname); - if (r < 0) - return r; - } else { - Manager *manager = link->manager; - - link_drop(link); - r = link_add(manager, m, &link); - if (r < 0) - return r; - } + link_drop(link); + r = link_add(manager, m, &link); + if (r < 0) + return r; } r = sd_netlink_message_read_u32(m, IFLA_MTU, &mtu); @@ -3823,7 +4018,7 @@ int link_save(Link *link) { assert(link->manager); if (link->state == LINK_STATE_LINGER) { - unlink(link->state_file); + (void) unlink(link->state_file); return 0; } @@ -3859,6 +4054,9 @@ int link_save(Link *link) { fprintf(f, "REQUIRED_FOR_ONLINE=%s\n", yes_no(link->network->required_for_online)); + fprintf(f, "REQUIRED_OPER_STATE_FOR_ONLINE=%s\n", + strempty(link_operstate_to_string(link->network->required_operstate_for_online))); + if (link->dhcp6_client) { r = sd_dhcp6_client_get_lease(link->dhcp6_client, &dhcp6_lease); if (r < 0 && r != -ENOMSG) @@ -3891,12 +4089,9 @@ int link_save(Link *link) { const struct in_addr *addresses; r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in_addrs(f, addresses, r); - space = true; - } + if (r > 0) + if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0) + space = true; } if (link->network->dhcp_use_dns && dhcp6_lease) { @@ -3937,12 +4132,9 @@ int link_save(Link *link) { const struct in_addr *addresses; r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); - if (r > 0) { - if (space) - fputc(' ', f); - serialize_in_addrs(f, addresses, r); - space = true; - } + if (r > 0) + if (serialize_in_addrs(f, addresses, r, space, in4_addr_is_non_local) > 0) + space = true; } if (link->network->dhcp_use_ntp && dhcp6_lease) { @@ -3974,9 +4166,7 @@ int link_save(Link *link) { (void) sd_dhcp6_lease_get_domains(dhcp6_lease, &dhcp6_domains); } - fputs("DOMAINS=", f); - space = false; - fputstrv(f, link->network->search_domains, NULL, &space); + ordered_set_print(f, "DOMAINS=", link->network->search_domains); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_YES) { NDiscDNSSL *dd; @@ -3994,9 +4184,7 @@ int link_save(Link *link) { fputc('\n', f); - fputs("ROUTE_DOMAINS=", f); - space = false; - fputstrv(f, link->network->route_domains, NULL, &space); + ordered_set_print(f, "ROUTE_DOMAINS=", link->network->route_domains); if (link->network->dhcp_use_domains == DHCP_USE_DOMAINS_ROUTE) { NDiscDNSSL *dd; @@ -4087,7 +4275,7 @@ int link_save(Link *link) { r = sd_dhcp_lease_get_address(link->dhcp_lease, &address); if (r >= 0) { fputs("DHCP4_ADDRESS=", f); - serialize_in_addrs(f, &address, 1); + serialize_in_addrs(f, &address, 1, false, NULL); fputc('\n', f); } @@ -4099,7 +4287,7 @@ int link_save(Link *link) { "DHCP_LEASE=%s\n", link->lease_file); } else - unlink(link->lease_file); + (void) unlink(link->lease_file); if (link->ipv4ll) { struct in_addr address; @@ -4107,7 +4295,7 @@ int link_save(Link *link) { r = sd_ipv4ll_get_address(link->ipv4ll, &address); if (r >= 0) { fputs("IPV4LL_ADDRESS=", f); - serialize_in_addrs(f, &address, 1); + serialize_in_addrs(f, &address, 1, false, NULL); fputc('\n', f); } } @@ -4171,14 +4359,3 @@ static const char* const link_state_table[_LINK_STATE_MAX] = { }; DEFINE_STRING_TABLE_LOOKUP(link_state, LinkState); - -static const char* const link_operstate_table[_LINK_OPERSTATE_MAX] = { - [LINK_OPERSTATE_OFF] = "off", - [LINK_OPERSTATE_NO_CARRIER] = "no-carrier", - [LINK_OPERSTATE_DORMANT] = "dormant", - [LINK_OPERSTATE_CARRIER] = "carrier", - [LINK_OPERSTATE_DEGRADED] = "degraded", - [LINK_OPERSTATE_ROUTABLE] = "routable", -}; - -DEFINE_STRING_TABLE_LOOKUP(link_operstate, LinkOperationalState); diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index dcb1ea68d..e65246c87 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -15,6 +15,8 @@ #include "sd-netlink.h" #include "list.h" +#include "log-link.h" +#include "network-util.h" #include "set.h" typedef enum LinkState { @@ -28,17 +30,6 @@ typedef enum LinkState { _LINK_STATE_INVALID = -1 } LinkState; -typedef enum LinkOperationalState { - LINK_OPERSTATE_OFF, - LINK_OPERSTATE_NO_CARRIER, - LINK_OPERSTATE_DORMANT, - LINK_OPERSTATE_CARRIER, - LINK_OPERSTATE_DEGRADED, - LINK_OPERSTATE_ROUTABLE, - _LINK_OPERSTATE_MAX, - _LINK_OPERSTATE_INVALID = -1 -} LinkOperationalState; - typedef struct Manager Manager; typedef struct Network Network; typedef struct Address Address; @@ -75,6 +66,8 @@ typedef struct Link { unsigned routing_policy_rule_messages; unsigned routing_policy_rule_remove_messages; unsigned enslaving; + /* link_is_enslaved() has additional checks. So, it is named _raw. */ + bool enslaved_raw; Set *addresses; Set *addresses_foreign; @@ -128,6 +121,7 @@ typedef struct Link { Hashmap *bound_by_links; Hashmap *bound_to_links; + Hashmap *slaves; } Link; typedef int (*link_netlink_message_handler_t)(sd_netlink*, sd_netlink_message*, Link*); @@ -150,7 +144,7 @@ int link_initialized(Link *link, sd_device *device); void link_check_ready(Link *link); -void link_update_operstate(Link *link); +void link_update_operstate(Link *link, bool also_update_bond_master); int link_update(Link *link, sd_netlink_message *message); void link_dirty(Link *link); @@ -162,7 +156,7 @@ bool link_has_carrier(Link *link); int link_ipv6ll_gained(Link *link, const struct in6_addr *address); -int link_set_mtu(Link *link, uint32_t mtu); +int link_set_mtu(Link *link, uint32_t mtu, bool force); int ipv4ll_configure(Link *link); int dhcp4_configure(Link *link); @@ -176,38 +170,15 @@ 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_; -const char* link_operstate_to_string(LinkOperationalState s) _const_; -LinkOperationalState link_operstate_from_string(const char *s) _pure_; - extern const sd_bus_vtable link_vtable[]; int link_node_enumerator(sd_bus *bus, const char *path, void *userdata, char ***nodes, sd_bus_error *error); 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_; -/* Macros which append INTERFACE= to the message */ - -#define log_link_full(link, level, error, ...) \ - ({ \ - const Link *_l = (link); \ - _l ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \ - log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ - }) \ - -#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__) -#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__) -#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__) -#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__) -#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__) - -#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__) -#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__) -#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__) -#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__) -#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__) - -#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__ -#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname +uint32_t link_get_vrf_table(Link *link); +uint32_t link_get_dhcp_route_table(Link *link); +uint32_t link_get_ipv6_accept_ra_route_table(Link *link); #define ADDRESS_FMT_VAL(address) \ be32toh((address).s_addr) >> 24, \ diff --git a/src/network/networkd-manager.c b/src/network/networkd-manager.c index c8d369e2a..9075b0a14 100644 --- a/src/network/networkd-manager.c +++ b/src/network/networkd-manager.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "sd-daemon.h" #include "sd-netlink.h" @@ -12,32 +13,27 @@ #include "bus-util.h" #include "conf-parser.h" #include "def.h" +#include "device-private.h" #include "device-util.h" #include "dns-domain.h" #include "fd-util.h" #include "fileio.h" #include "local-addresses.h" #include "netlink-util.h" +#include "network-internal.h" #include "networkd-manager.h" #include "ordered-set.h" #include "path-util.h" #include "set.h" #include "strv.h" +#include "sysctl-util.h" #include "tmpfile-util.h" +#include "udev-util.h" #include "virt.h" /* use 8 MB for receive socket kernel queue. */ #define RCVBUF_SIZE (8*1024*1024) -const char* const network_dirs[] = { - "/etc/systemd/network", - "/run/systemd/network", - "/usr/lib/systemd/network", -#if HAVE_SPLIT_USR - "/lib/systemd/network", -#endif - NULL}; - static int setup_default_address_pool(Manager *m) { AddressPool *p; int r; @@ -46,11 +42,11 @@ static int setup_default_address_pool(Manager *m) { /* Add in the well-known private address ranges. */ - r = address_pool_new_from_string(m, &p, AF_INET6, "fc00::", 7); + r = address_pool_new_from_string(m, &p, AF_INET6, "fd00::", 8); if (r < 0) return r; - r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16); + r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8); if (r < 0) return r; @@ -58,7 +54,7 @@ static int setup_default_address_pool(Manager *m) { if (r < 0) return r; - r = address_pool_new_from_string(m, &p, AF_INET, "10.0.0.0", 8); + r = address_pool_new_from_string(m, &p, AF_INET, "192.168.0.0", 16); if (r < 0) return r; @@ -188,21 +184,21 @@ int manager_connect_bus(Manager *m) { static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *device, void *userdata) { Manager *m = userdata; - const char *action; + DeviceAction action; Link *link = NULL; int r, ifindex; assert(m); assert(device); - r = sd_device_get_property_value(device, "ACTION", &action); + r = device_get_action(device, &action); if (r < 0) { - log_device_debug_errno(device, r, "Failed to get 'ACTION' property, ignoring device: %m"); + log_device_debug_errno(device, r, "Failed to get udev action, ignoring device: %m"); return 0; } - if (!STR_IN_SET(action, "add", "change")) { - log_device_debug(device, "Ignoring udev %s event for device.", action); + if (!IN_SET(action, DEVICE_ACTION_ADD, DEVICE_ACTION_CHANGE, DEVICE_ACTION_MOVE)) { + log_device_debug(device, "Ignoring udev %s event for device.", device_action_to_string(action)); return 0; } @@ -212,6 +208,17 @@ static int manager_udev_process_link(sd_device_monitor *monitor, sd_device *devi return 0; } + r = device_is_renaming(device); + if (r < 0) { + log_device_error_errno(device, r, "Failed to determine the device is renamed or not, ignoring '%s' uevent: %m", + device_action_to_string(action)); + return 0; + } + if (r > 0) { + log_device_debug(device, "Interface is under renaming, wait for the interface to be renamed: %m"); + return 0; + } + r = link_get(m, ifindex, &link); if (r < 0) { if (r != -ENODEV) @@ -422,6 +429,28 @@ int manager_rtnl_process_route(sd_netlink *rtnl, sd_netlink_message *message, vo (void) route_get(link, family, &dst, dst_prefixlen, tos, priority, table, &route); + if (DEBUG_LOGGING) { + _cleanup_free_ char *buf_dst = NULL, *buf_dst_prefixlen = NULL, + *buf_src = NULL, *buf_gw = NULL, *buf_prefsrc = NULL; + + if (!in_addr_is_null(family, &dst)) { + (void) in_addr_to_string(family, &dst, &buf_dst); + (void) asprintf(&buf_dst_prefixlen, "/%u", dst_prefixlen); + } + if (!in_addr_is_null(family, &src)) + (void) in_addr_to_string(family, &src, &buf_src); + if (!in_addr_is_null(family, &gw)) + (void) in_addr_to_string(family, &gw, &buf_gw); + if (!in_addr_is_null(family, &prefsrc)) + (void) in_addr_to_string(family, &prefsrc, &buf_prefsrc); + + log_link_debug(link, + "%s route: dst: %s%s, src: %s, gw: %s, prefsrc: %s", + type == RTM_DELROUTE ? "Removing" : route ? "Updating" : "Adding", + strna(buf_dst), strempty(buf_dst_prefixlen), + strna(buf_src), strna(buf_gw), strna(buf_prefsrc)); + } + switch (type) { case RTM_NEWROUTE: if (!route) { @@ -1022,14 +1051,19 @@ static int ordered_set_put_in4_addr(OrderedSet *s, const struct in_addr *address return r; } -static int ordered_set_put_in4_addrv(OrderedSet *s, const struct in_addr *addresses, unsigned n) { +static int ordered_set_put_in4_addrv(OrderedSet *s, + const struct in_addr *addresses, + size_t n, + bool (*predicate)(const struct in_addr *addr)) { int r, c = 0; - unsigned i; + size_t i; assert(s); assert(n == 0 || addresses); for (i = 0; i < n; i++) { + if (predicate && !predicate(&addresses[i])) + continue; r = ordered_set_put_in4_addr(s, addresses+i); if (r < 0) return r; @@ -1040,22 +1074,6 @@ static int ordered_set_put_in4_addrv(OrderedSet *s, const struct in_addr *addres return c; } -static void print_string_set(FILE *f, const char *field, OrderedSet *s) { - bool space = false; - Iterator i; - char *p; - - if (ordered_set_isempty(s)) - return; - - fputs(field, f); - - ORDERED_SET_FOREACH(p, s, i) - fputs_with_space(f, p, NULL, &space); - - fputc('\n', f); -} - static int manager_save(Manager *m) { _cleanup_ordered_set_free_free_ OrderedSet *dns = NULL, *ntp = NULL, *search_domains = NULL, *route_domains = NULL; Link *link; @@ -1105,11 +1123,11 @@ static int manager_save(Manager *m) { if (r < 0) return r; - r = ordered_set_put_strdupv(search_domains, link->network->search_domains); + r = ordered_set_put_string_set(search_domains, link->network->search_domains); if (r < 0) return r; - r = ordered_set_put_strdupv(route_domains, link->network->route_domains); + r = ordered_set_put_string_set(route_domains, link->network->route_domains); if (r < 0) return r; @@ -1122,7 +1140,7 @@ static int manager_save(Manager *m) { r = sd_dhcp_lease_get_dns(link->dhcp_lease, &addresses); if (r > 0) { - r = ordered_set_put_in4_addrv(dns, addresses, r); + r = ordered_set_put_in4_addrv(dns, addresses, r, in4_addr_is_non_local); if (r < 0) return r; } else if (r < 0 && r != -ENODATA) @@ -1134,7 +1152,7 @@ static int manager_save(Manager *m) { r = sd_dhcp_lease_get_ntp(link->dhcp_lease, &addresses); if (r > 0) { - r = ordered_set_put_in4_addrv(ntp, addresses, r); + r = ordered_set_put_in4_addrv(ntp, addresses, r, in4_addr_is_non_local); if (r < 0) return r; } else if (r < 0 && r != -ENODATA) @@ -1178,10 +1196,10 @@ static int manager_save(Manager *m) { "# This is private data. Do not parse.\n" "OPER_STATE=%s\n", operstate_str); - print_string_set(f, "DNS=", dns); - print_string_set(f, "NTP=", ntp); - print_string_set(f, "DOMAINS=", search_domains); - print_string_set(f, "ROUTE_DOMAINS=", route_domains); + ordered_set_print(f, "DNS=", dns); + ordered_set_print(f, "NTP=", ntp); + ordered_set_print(f, "DOMAINS=", search_domains); + ordered_set_print(f, "ROUTE_DOMAINS=", route_domains); r = routing_policy_serialize_rules(m->rules, f); if (r < 0) @@ -1360,6 +1378,8 @@ int manager_new(Manager **ret) { if (!m->state_file) return -ENOMEM; + m->sysctl_ipv6_enabled = -1; + r = sd_event_default(&m->event); if (r < 0) return r; @@ -1421,9 +1441,6 @@ void manager_free(Manager *m) { sd_netlink_unref(m->genl); sd_resolve_unref(m->resolve); - while ((network = m->networks)) - network_free(network); - while ((link = hashmap_first(m->dhcp6_prefixes))) manager_dhcp6_prefix_remove_all(m, link); hashmap_free(m->dhcp6_prefixes); @@ -1439,6 +1456,9 @@ void manager_free(Manager *m) { m->links_requesting_uuid = set_free(m->links_requesting_uuid); set_free(m->duids_requesting_uuid); + while ((network = m->networks)) + network_free(network); + hashmap_free(m->networks_by_name); m->netdevs = hashmap_free_with_destructor(m->netdevs, netdev_unref); @@ -1485,7 +1505,7 @@ int manager_load_config(Manager *m) { int r; /* update timestamp */ - paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, true); + paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, true); r = netdev_load(m); if (r < 0) @@ -1499,7 +1519,7 @@ int manager_load_config(Manager *m) { } bool manager_should_reload(Manager *m) { - return paths_check_timestamp(network_dirs, &m->network_dirs_ts_usec, false); + return paths_check_timestamp(NETWORK_DIRS, &m->network_dirs_ts_usec, false); } int manager_rtnl_enumerate_links(Manager *m) { @@ -1858,3 +1878,18 @@ int manager_request_product_uuid(Manager *m, Link *link) { return 0; } + +int manager_sysctl_ipv6_enabled(Manager *manager) { + _cleanup_free_ char *value = NULL; + int r; + + if (manager->sysctl_ipv6_enabled >= 0) + return manager->sysctl_ipv6_enabled; + + r = sysctl_read_ip_property(AF_INET6, "all", "disable_ipv6", &value); + if (r < 0) + return log_warning_errno(r, "Failed to read net.ipv6.conf.all.disable_ipv6 sysctl property: %m"); + + manager->sysctl_ipv6_enabled = value[0] == '0'; + return manager->sysctl_ipv6_enabled; +} diff --git a/src/network/networkd-manager.h b/src/network/networkd-manager.h index 289ca9621..35ab6bedb 100644 --- a/src/network/networkd-manager.h +++ b/src/network/networkd-manager.h @@ -18,8 +18,6 @@ #include "networkd-link.h" #include "networkd-network.h" -extern const char* const network_dirs[]; - struct Manager { sd_netlink *rtnl; /* lazy initialized */ @@ -58,6 +56,8 @@ struct Manager { Set *rules; Set *rules_foreign; Set *rules_saved; + + int sysctl_ipv6_enabled; }; extern const sd_bus_vtable manager_vtable[]; @@ -95,4 +95,6 @@ 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_all(Manager *m, Link *link); +int manager_sysctl_ipv6_enabled(Manager *manager); + DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); diff --git a/src/network/networkd-ndisc.c b/src/network/networkd-ndisc.c index e5b8d1155..eb470a4d4 100644 --- a/src/network/networkd-ndisc.c +++ b/src/network/networkd-ndisc.c @@ -103,7 +103,7 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) { 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; + route->table = link_get_ipv6_accept_ra_route_table(link); route->priority = link->network->dhcp_route_metric; route->protocol = RTPROT_RA; route->pref = preference; @@ -238,7 +238,7 @@ static int ndisc_router_process_onlink_prefix(Link *link, sd_ndisc_router *rt) { 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; + route->table = link_get_ipv6_accept_ra_route_table(link); route->priority = link->network->dhcp_route_metric; route->protocol = RTPROT_RA; route->flags = RTM_F_PREFIX; @@ -299,7 +299,7 @@ static int ndisc_router_process_route(Link *link, sd_ndisc_router *rt) { 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; + route->table = link_get_ipv6_accept_ra_route_table(link); route->protocol = RTPROT_RA; route->pref = preference; route->gw.in6 = gateway; @@ -493,7 +493,7 @@ static void ndisc_router_process_dnssl(Link *link, sd_ndisc_router *rt) { } } -static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { +static int ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { int r; assert(link); @@ -503,18 +503,14 @@ static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { for (;;) { uint8_t type; - if (r < 0) { - log_link_warning_errno(link, r, "Failed to iterate through options: %m"); - return; - } + if (r < 0) + return log_link_warning_errno(link, r, "Failed to iterate through options: %m"); if (r == 0) /* EOF */ break; r = sd_ndisc_router_option_get_type(rt, &type); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA option type: %m"); - return; - } + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get RA option type: %m"); switch (type) { @@ -522,14 +518,15 @@ static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { uint8_t flags; r = sd_ndisc_router_prefix_get_flags(rt, &flags); - if (r < 0) { - log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); - return; - } + if (r < 0) + return log_link_warning_errno(link, r, "Failed to get RA prefix flags: %m"); - if (flags & ND_OPT_PI_FLAG_ONLINK) + if (link->network->ipv6_accept_ra_use_onlink_prefix && + FLAGS_SET(flags, ND_OPT_PI_FLAG_ONLINK)) (void) ndisc_router_process_onlink_prefix(link, rt); - if (flags & ND_OPT_PI_FLAG_AUTO) + + if (link->network->ipv6_accept_ra_use_autonomous_prefix && + FLAGS_SET(flags, ND_OPT_PI_FLAG_AUTO)) (void) ndisc_router_process_autonomous_prefix(link, rt); break; @@ -552,6 +549,8 @@ static void ndisc_router_process_options(Link *link, sd_ndisc_router *rt) { r = sd_ndisc_router_option_next(rt); } + + return 0; } static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { @@ -578,8 +577,8 @@ static int ndisc_router_handler(Link *link, sd_ndisc_router *rt) { } } - ndisc_router_process_default(link, rt); - ndisc_router_process_options(link, rt); + (void) ndisc_router_process_default(link, rt); + (void) ndisc_router_process_options(link, rt); return r; } diff --git a/src/network/networkd-ndisc.h b/src/network/networkd-ndisc.h index 762f42d43..0b614bf50 100644 --- a/src/network/networkd-ndisc.h +++ b/src/network/networkd-ndisc.h @@ -2,6 +2,7 @@ #pragma once #include "networkd-link.h" +#include "time-util.h" typedef struct NDiscRDNSS { usec_t valid_until; diff --git a/src/network/networkd-neighbor.c b/src/network/networkd-neighbor.c index 254a60bdc..d0275fdd3 100644 --- a/src/network/networkd-neighbor.c +++ b/src/network/networkd-neighbor.c @@ -137,20 +137,9 @@ int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_hand 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_message_append_in_addr_union(req, NDA_DST, neighbor->family, &neighbor->in_addr); + if (r < 0) + return log_error_errno(r, "Could not append NDA_DST attribute: %m"); r = netlink_call_async(link->manager->rtnl, NULL, req, callback ?: neighbor_handler, link_netlink_destroy_callback, link); @@ -175,7 +164,7 @@ int config_parse_neighbor_address(const char *unit, void *userdata) { Network *network = userdata; - _cleanup_(neighbor_freep) Neighbor *n = NULL; + _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; int r; assert(filename); @@ -211,7 +200,7 @@ int config_parse_neighbor_hwaddr(const char *unit, void *userdata) { Network *network = userdata; - _cleanup_(neighbor_freep) Neighbor *n = NULL; + _cleanup_(neighbor_free_or_set_invalidp) Neighbor *n = NULL; int r; assert(filename); diff --git a/src/network/networkd-neighbor.h b/src/network/networkd-neighbor.h index 094bf7977..f591f0b03 100644 --- a/src/network/networkd-neighbor.h +++ b/src/network/networkd-neighbor.h @@ -13,6 +13,7 @@ typedef struct Neighbor Neighbor; #include "networkd-link.h" #include "networkd-network.h" +#include "networkd-util.h" struct Neighbor { Network *network; @@ -29,7 +30,7 @@ struct Neighbor { void neighbor_free(Neighbor *neighbor); -DEFINE_TRIVIAL_CLEANUP_FUNC(Neighbor*, neighbor_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(Neighbor, neighbor_free); int neighbor_configure(Neighbor *neighbor, Link *link, link_netlink_message_handler_t callback); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 5d8aede59..47a9f7d80 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -25,28 +25,29 @@ Match.Path, config_parse_strv, Match.Driver, config_parse_strv, 0, offsetof(Network, match_driver) Match.Type, config_parse_strv, 0, offsetof(Network, match_type) Match.Name, config_parse_ifnames, 0, offsetof(Network, match_name) -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, match_kernel_cmdline) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, match_kernel_version) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, match_arch) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(Network, conditions) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(Network, conditions) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(Network, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions) Link.MACAddress, config_parse_hwaddr, 0, offsetof(Network, mac) Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu) Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) Link.Multicast, config_parse_tristate, 0, offsetof(Network, multicast) Link.AllMulticast, config_parse_tristate, 0, offsetof(Network, allmulticast) Link.Unmanaged, config_parse_bool, 0, offsetof(Network, unmanaged) -Link.RequiredForOnline, config_parse_bool, 0, offsetof(Network, required_for_online) +Link.RequiredForOnline, config_parse_required_for_online, 0, 0 Network.Description, config_parse_string, 0, offsetof(Network, description) -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 -Network.IPVLAN, config_parse_netdev, 0, 0 -Network.VXLAN, config_parse_netdev, 0, 0 -Network.Tunnel, config_parse_tunnel, 0, 0 -Network.VRF, config_parse_netdev, 0, 0 +Network.Bridge, config_parse_ifname, 0, offsetof(Network, bridge_name) +Network.Bond, config_parse_ifname, 0, offsetof(Network, bond_name) +Network.VLAN, config_parse_stacked_netdev, NETDEV_KIND_VLAN, offsetof(Network, stacked_netdev_names) +Network.MACVLAN, config_parse_stacked_netdev, NETDEV_KIND_MACVLAN, offsetof(Network, stacked_netdev_names) +Network.MACVTAP, config_parse_stacked_netdev, NETDEV_KIND_MACVTAP, offsetof(Network, stacked_netdev_names) +Network.IPVLAN, config_parse_stacked_netdev, NETDEV_KIND_IPVLAN, offsetof(Network, stacked_netdev_names) +Network.VXLAN, config_parse_stacked_netdev, NETDEV_KIND_VXLAN, offsetof(Network, stacked_netdev_names) +Network.L2TP, config_parse_stacked_netdev, NETDEV_KIND_L2TP, offsetof(Network, stacked_netdev_names) +Network.Tunnel, config_parse_stacked_netdev, _NETDEV_KIND_TUNNEL, offsetof(Network, stacked_netdev_names) +Network.VRF, config_parse_ifname, 0, offsetof(Network, vrf_name) Network.DHCP, config_parse_dhcp, 0, offsetof(Network, dhcp) Network.DHCPServer, config_parse_bool, 0, offsetof(Network, dhcp_server) Network.LinkLocalAddressing, config_parse_address_family_boolean, 0, offsetof(Network, link_local) @@ -81,6 +82,7 @@ Network.ProxyARP, config_parse_tristate, Network.IPv6ProxyNDPAddress, config_parse_ipv6_proxy_ndp_address, 0, 0 Network.BindCarrier, config_parse_strv, 0, offsetof(Network, bind_carrier) Network.ConfigureWithoutCarrier, config_parse_bool, 0, offsetof(Network, configure_without_carrier) +Network.IgnoreCarrierLoss, config_parse_bool, 0, offsetof(Network, ignore_carrier_loss) Address.Address, config_parse_address, 0, 0 Address.Peer, config_parse_address, 0, 0 Address.Broadcast, config_parse_broadcast, 0, 0 @@ -116,6 +118,7 @@ Route.Scope, config_parse_route_scope, Route.PreferredSource, config_parse_preferred_src, 0, 0 Route.Table, config_parse_route_table, 0, 0 Route.MTUBytes, config_parse_route_mtu, AF_UNSPEC, 0 +Route.GatewayOnLink, config_parse_gateway_onlink, 0, 0 Route.GatewayOnlink, config_parse_gateway_onlink, 0, 0 Route.IPv6Preference, config_parse_ipv6_route_preference, 0, 0 Route.Protocol, config_parse_route_protocol, 0, 0 @@ -140,15 +143,17 @@ DHCP.UserClass, config_parse_dhcp_user_class, DHCP.DUIDType, config_parse_duid_type, 0, offsetof(Network, duid) DHCP.DUIDRawData, config_parse_duid_rawdata, 0, offsetof(Network, duid) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) -DHCP.RouteTable, config_parse_dhcp_route_table, 0, 0 +DHCP.RouteTable, config_parse_section_route_table, 0, 0 DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_use_timezone) DHCP.IAID, config_parse_iaid, 0, 0 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.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) +IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix) IPv6AcceptRA.UseDNS, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_dns) IPv6AcceptRA.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, ipv6_accept_ra_use_domains) -IPv6AcceptRA.RouteTable, config_parse_uint32, 0, offsetof(Network, ipv6_accept_ra_route_table) +IPv6AcceptRA.RouteTable, config_parse_section_route_table, 0, 0 DHCPServer.MaxLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_max_lease_time_usec) DHCPServer.DefaultLeaseTimeSec, config_parse_sec, 0, offsetof(Network, dhcp_server_default_lease_time_usec) DHCPServer.EmitDNS, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_dns) @@ -166,7 +171,10 @@ 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.MulticastFlood, config_parse_tristate, 0, offsetof(Network, multicast_flood) Bridge.MulticastToUnicast, config_parse_tristate, 0, offsetof(Network, multicast_to_unicast) +Bridge.NeighborSuppression, config_parse_tristate, 0, offsetof(Network, neighbor_suppression) +Bridge.Learning, config_parse_tristate, 0, offsetof(Network, learning) Bridge.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 @@ -191,6 +199,7 @@ IPv6Prefix.PreferredLifetimeSec, config_parse_prefix_lifetime, CAN.BitRate, config_parse_si_size, 0, offsetof(Network, can_bitrate) CAN.SamplePoint, config_parse_permille, 0, offsetof(Network, can_sample_point) CAN.RestartSec, config_parse_sec, 0, offsetof(Network, can_restart_us) +CAN.TripleSampling, config_parse_tristate, 0, offsetof(Network, can_triple_sampling) /* backwards compatibility: do not add new entries to this section */ Network.IPv4LL, config_parse_ipv4ll, 0, offsetof(Network, link_local) DHCPv4.UseDNS, config_parse_bool, 0, offsetof(Network, dhcp_use_dns) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 12344ec69..836776ae8 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -16,47 +16,15 @@ #include "networkd-network.h" #include "parse-util.h" #include "set.h" +#include "socket-util.h" #include "stat-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" #include "util.h" -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 NetworkConfigSection *x, const NetworkConfigSection *y) { - int r; - - r = strcmp(x->filename, y->filename); - if (r != 0) - return r; - - return CMP(x->line, y->line); -} - -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; - - cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1); - if (!cs) - return -ENOMEM; - - strcpy(cs->filename, filename); - cs->line = line; - - *s = TAKE_PTR(cs); - - return 0; -} - -void network_config_section_free(NetworkConfigSection *cs) { - free(cs); -} +/* Let's assume that anything above this number is a user misconfiguration. */ +#define MAX_NTP_SERVERS 128 /* Set defaults following RFC7844 */ void network_apply_anonymize_if_set(Network *network) { @@ -97,13 +65,229 @@ void network_apply_anonymize_if_set(Network *network) { network->dhcp_use_timezone = false; } +static int network_resolve_netdev_one(Network *network, const char *name, NetDevKind kind, NetDev **ret_netdev) { + const char *kind_string; + NetDev *netdev; + int r; + + /* For test-networkd-conf, the check must be earlier than the assertions. */ + if (!name) + return 0; + + assert(network); + assert(network->manager); + assert(network->filename); + assert(ret_netdev); + + if (kind == _NETDEV_KIND_TUNNEL) + kind_string = "tunnel"; + else { + kind_string = netdev_kind_to_string(kind); + if (!kind_string) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: Invalid NetDev kind of %s, ignoring assignment.", + network->filename, name); + } + + r = netdev_get(network->manager, name, &netdev); + if (r < 0) + return log_error_errno(r, "%s: %s NetDev could not be found, ignoring assignment.", + network->filename, name); + + if (netdev->kind != kind && !(kind == _NETDEV_KIND_TUNNEL && + IN_SET(netdev->kind, + NETDEV_KIND_IPIP, + NETDEV_KIND_SIT, + NETDEV_KIND_GRE, + NETDEV_KIND_GRETAP, + NETDEV_KIND_IP6GRE, + NETDEV_KIND_IP6GRETAP, + NETDEV_KIND_VTI, + NETDEV_KIND_VTI6, + NETDEV_KIND_IP6TNL, + NETDEV_KIND_ERSPAN))) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: NetDev %s is not a %s, ignoring assignment", + network->filename, name, kind_string); + + *ret_netdev = netdev_ref(netdev); + return 1; +} + +static int network_resolve_stacked_netdevs(Network *network) { + void *name, *kind; + Iterator i; + int r; + + assert(network); + + HASHMAP_FOREACH_KEY(kind, name, network->stacked_netdev_names, i) { + _cleanup_(netdev_unrefp) NetDev *netdev = NULL; + + r = network_resolve_netdev_one(network, name, PTR_TO_INT(kind), &netdev); + if (r <= 0) + continue; + + 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) + return log_error_errno(r, "%s: Failed to add NetDev '%s' to network: %m", + network->filename, (const char *) name); + + netdev = NULL; + } + + return 0; +} + +static uint32_t network_get_stacked_netdevs_mtu(Network *network) { + uint32_t mtu = 0; + NetDev *dev; + Iterator i; + + HASHMAP_FOREACH(dev, network->stacked_netdevs, i) + if (dev->kind == NETDEV_KIND_VLAN && dev->mtu > 0) + /* See vlan_dev_change_mtu() in kernel. + * Note that the additional 4bytes may not be necessary for all devices. */ + mtu = MAX(mtu, dev->mtu + 4); + + else if (dev->kind == NETDEV_KIND_MACVLAN && dev->mtu > mtu) + /* See macvlan_change_mtu() in kernel. */ + mtu = dev->mtu; + + return mtu; +} + +int network_verify(Network *network) { + Address *address, *address_next; + Route *route, *route_next; + FdbEntry *fdb, *fdb_next; + Neighbor *neighbor, *neighbor_next; + AddressLabel *label, *label_next; + Prefix *prefix, *prefix_next; + RoutingPolicyRule *rule, *rule_next; + uint32_t mtu; + + assert(network); + assert(network->filename); + + /* skip out early if configuration does not match the environment */ + if (!condition_test_list(network->conditions, NULL, NULL, NULL)) + return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: Conditions in the file do not match the system environment, skipping.", + network->filename); + + (void) network_resolve_netdev_one(network, network->bond_name, NETDEV_KIND_BOND, &network->bond); + (void) network_resolve_netdev_one(network, network->bridge_name, NETDEV_KIND_BRIDGE, &network->bridge); + (void) network_resolve_netdev_one(network, network->vrf_name, NETDEV_KIND_VRF, &network->vrf); + (void) network_resolve_stacked_netdevs(network); + + /* Free unnecessary entries. */ + network->bond_name = mfree(network->bond_name); + network->bridge_name = mfree(network->bridge_name); + network->vrf_name = mfree(network->vrf_name); + network->stacked_netdev_names = hashmap_free_free_key(network->stacked_netdev_names); + + if (network->bond) { + /* Bonding slave does not support addressing. */ + if (network->ipv6_accept_ra > 0) { + log_warning("%s: Cannot enable IPv6AcceptRA= when Bond= is specified, disabling IPv6AcceptRA=.", + network->filename); + network->ipv6_accept_ra = 0; + } + if (network->link_local >= 0 && network->link_local != ADDRESS_FAMILY_NO) { + log_warning("%s: Cannot enable LinkLocalAddressing= when Bond= is specified, disabling LinkLocalAddressing=.", + network->filename); + network->link_local = ADDRESS_FAMILY_NO; + } + if (network->dhcp != ADDRESS_FAMILY_NO) { + log_warning("%s: Cannot enable DHCP= when Bond= is specified, disabling DHCP=.", + network->filename); + network->dhcp = ADDRESS_FAMILY_NO; + } + if (network->dhcp_server) { + log_warning("%s: Cannot enable DHCPServer= when Bond= is specified, disabling DHCPServer=.", + network->filename); + network->dhcp_server = false; + } + if (network->n_static_addresses > 0) { + log_warning("%s: Cannot set addresses when Bond= is specified, ignoring addresses.", + network->filename); + while ((address = network->static_addresses)) + address_free(address); + } + if (network->n_static_routes > 0) { + log_warning("%s: Cannot set routes when Bond= is specified, ignoring routes.", + network->filename); + while ((route = network->static_routes)) + route_free(route); + } + } + + if (network->link_local < 0) + network->link_local = network->bridge ? ADDRESS_FAMILY_NO : ADDRESS_FAMILY_IPV6; + + if (network->ipv6_accept_ra < 0 && network->bridge) + network->ipv6_accept_ra = false; + + /* IPMasquerade=yes implies IPForward=yes */ + if (network->ip_masquerade) + network->ip_forward |= ADDRESS_FAMILY_IPV4; + + network->mtu_is_set = network->mtu > 0; + mtu = network_get_stacked_netdevs_mtu(network); + if (network->mtu < mtu) { + if (network->mtu_is_set) + log_notice("%s: Bumping MTUBytes= from %"PRIu32" to %"PRIu32" because of stacked device", + network->filename, network->mtu, mtu); + network->mtu = mtu; + } + + if (network->mtu_is_set && network->dhcp_use_mtu) { + log_warning("%s: MTUBytes= in [Link] section and UseMTU= in [DHCP] section are set. " + "Disabling UseMTU=.", network->filename); + network->dhcp_use_mtu = false; + } + + LIST_FOREACH_SAFE(addresses, address, address_next, network->static_addresses) + if (address_section_verify(address) < 0) + address_free(address); + + LIST_FOREACH_SAFE(routes, route, route_next, network->static_routes) + if (route_section_verify(route, network) < 0) + route_free(route); + + LIST_FOREACH_SAFE(static_fdb_entries, fdb, fdb_next, network->static_fdb_entries) + if (section_is_invalid(fdb->section)) + fdb_entry_free(fdb); + + LIST_FOREACH_SAFE(neighbors, neighbor, neighbor_next, network->neighbors) + if (section_is_invalid(neighbor->section)) + neighbor_free(neighbor); + + LIST_FOREACH_SAFE(labels, label, label_next, network->address_labels) + if (section_is_invalid(label->section)) + address_label_free(label); + + LIST_FOREACH_SAFE(prefixes, prefix, prefix_next, network->static_prefixes) + if (section_is_invalid(prefix->section)) + prefix_free(prefix); + + LIST_FOREACH_SAFE(rules, rule, rule_next, network->rules) + if (section_is_invalid(rule->section)) + routing_policy_rule_free(rule); + + return 0; +} + int network_load_one(Manager *manager, const char *filename) { _cleanup_free_ char *fname = NULL, *name = NULL; _cleanup_(network_freep) Network *network = NULL; _cleanup_fclose_ FILE *file = NULL; const char *dropin_dirname; - Address *address; - Route *route; char *d; int r; @@ -144,11 +328,11 @@ int network_load_one(Manager *manager, const char *filename) { return log_oom(); *network = (Network) { - .manager = manager, .filename = TAKE_PTR(fname), .name = TAKE_PTR(name), .required_for_online = true, + .required_operstate_for_online = LINK_OPERSTATE_DEGRADED, .dhcp = ADDRESS_FAMILY_NO, .dhcp_use_ntp = true, .dhcp_use_dns = true, @@ -182,7 +366,10 @@ int network_load_one(Manager *manager, const char *filename) { .fast_leave = -1, .allow_port_to_be_root = -1, .unicast_flood = -1, + .multicast_flood = -1, .multicast_to_unicast = -1, + .neighbor_suppression = -1, + .learning = -1, .priority = LINK_BRIDGE_PORT_PRIORITY_INVALID, .lldp_mode = LLDP_MODE_ROUTERS_ONLY, @@ -193,7 +380,8 @@ int network_load_one(Manager *manager, const char *filename) { .dnssec_mode = _DNSSEC_MODE_INVALID, .dns_over_tls_mode = _DNS_OVER_TLS_MODE_INVALID, - .link_local = ADDRESS_FAMILY_IPV6, + /* If LinkLocalAddressing= is not set, then set to ADDRESS_FAMILY_IPV6 later. */ + .link_local = _ADDRESS_FAMILY_BOOLEAN_INVALID, .ipv6_privacy_extensions = IPV6_PRIVACY_EXTENSIONS_NO, .ipv6_accept_ra = -1, @@ -206,10 +394,15 @@ int network_load_one(Manager *manager, const char *filename) { .multicast = -1, .allmulticast = -1, .ipv6_accept_ra_use_dns = true, + .ipv6_accept_ra_use_autonomous_prefix = true, + .ipv6_accept_ra_use_onlink_prefix = true, .ipv6_accept_ra_route_table = RT_TABLE_MAIN, + .ipv6_accept_ra_route_table_set = false, + + .can_triple_sampling = -1, }; - r = config_parse_many(filename, network_dirs, dropin_dirname, + r = config_parse_many(filename, NETWORK_DIRS, dropin_dirname, "Match\0" "Link\0" "Network\0" @@ -231,25 +424,17 @@ int network_load_one(Manager *manager, const char *filename) { "CAN\0", config_item_perf_lookup, network_network_gperf_lookup, CONFIG_PARSE_WARN, network); - if (r < 0) { - /* Unset manager here. Otherwise, LIST_REMOVE() in network_free() fails. */ - network->manager = NULL; + if (r < 0) return r; - } network_apply_anonymize_if_set(network); - /* IPMasquerade=yes implies IPForward=yes */ - 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; - } + r = network_add_ipv4ll_route(network); + if (r < 0) + log_warning_errno(r, "%s: Failed to add IPv4LL route, ignoring: %m", network->filename); LIST_PREPEND(networks, manager->networks, network); + network->manager = manager; r = hashmap_ensure_allocated(&manager->networks_by_name, &string_hash_ops); if (r < 0) @@ -259,22 +444,10 @@ int network_load_one(Manager *manager, const char *filename) { if (r < 0) return r; - LIST_FOREACH(routes, route, network->static_routes) - if (!route->family) { - log_warning("Route section without Gateway field configured in %s. " - "Ignoring", filename); - return 0; - } - - LIST_FOREACH(addresses, address, network->static_addresses) - if (!address->family) { - log_warning("Address section without Address field configured in %s. " - "Ignoring", filename); - return 0; - } + if (network_verify(network) < 0) + return 0; network = NULL; - return 0; } @@ -289,7 +462,7 @@ int network_load(Manager *manager) { while ((network = manager->networks)) network_free(network); - r = conf_files_list_strv(&files, ".network", NULL, 0, network_dirs); + r = conf_files_list_strv(&files, ".network", NULL, 0, NETWORK_DIRS); if (r < 0) return log_error_errno(r, "Failed to enumerate network files: %m"); @@ -322,6 +495,7 @@ void network_free(Network *network) { strv_free(network->match_driver); strv_free(network->match_type); strv_free(network->match_name); + condition_free_list(network->conditions); free(network->description); free(network->dhcp_vendor_class_identifier); @@ -332,17 +506,20 @@ void network_free(Network *network) { strv_free(network->ntp); free(network->dns); - strv_free(network->search_domains); - strv_free(network->route_domains); + ordered_set_free_free(network->search_domains); + ordered_set_free_free(network->route_domains); strv_free(network->bind_carrier); - strv_free(network->router_search_domains); + ordered_set_free_free(network->router_search_domains); free(network->router_dns); + free(network->bridge_name); + free(network->bond_name); + free(network->vrf_name); + hashmap_free_free_key(network->stacked_netdev_names); netdev_unref(network->bridge); netdev_unref(network->bond); netdev_unref(network->vrf); - hashmap_free_with_destructor(network->stacked_netdevs, netdev_unref); while ((route = network->static_routes)) @@ -390,12 +567,6 @@ void network_free(Network *network) { free(network->name); - condition_free_list(network->match_host); - condition_free_list(network->match_virt); - condition_free_list(network->match_kernel_cmdline); - condition_free_list(network->match_kernel_version); - condition_free_list(network->match_arch); - free(network->dhcp_server_timezone); free(network->dhcp_server_dns); free(network->dhcp_server_ntp); @@ -424,8 +595,7 @@ int network_get_by_name(Manager *manager, const char *name, Network **ret) { int network_get(Manager *manager, sd_device *device, const char *ifname, const struct ether_addr *address, Network **ret) { - const char *path = NULL, *parent_driver = NULL, *driver = NULL, *devtype = NULL; - sd_device *parent; + const char *path = NULL, *driver = NULL, *devtype = NULL; Network *network; assert(manager); @@ -434,9 +604,6 @@ int network_get(Manager *manager, sd_device *device, if (device) { (void) sd_device_get_property_value(device, "ID_PATH", &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", &driver); (void) sd_device_get_devtype(device, &devtype); @@ -445,11 +612,8 @@ int network_get(Manager *manager, sd_device *device, LIST_FOREACH(networks, network, manager->networks) { if (net_match_config(network->match_mac, network->match_path, network->match_driver, network->match_type, - network->match_name, network->match_host, - network->match_virt, network->match_kernel_cmdline, - network->match_kernel_version, network->match_arch, - address, path, parent_driver, driver, - devtype, ifname)) { + network->match_name, + address, path, driver, devtype, ifname)) { if (network->match_name && device) { const char *attr; uint8_t name_assign_type = NET_NAME_UNKNOWN; @@ -476,37 +640,15 @@ int network_get(Manager *manager, sd_device *device, } int network_apply(Network *network, Link *link) { - int r; - assert(network); assert(link); link->network = network; - if (network->ipv4ll_route) { - Route *route; - - r = route_new_static(network, NULL, 0, &route); - if (r < 0) - return r; - - r = inet_pton(AF_INET, "169.254.0.0", &route->dst.in); - if (r == 0) - return -EINVAL; - if (r < 0) - return -errno; - - route->family = AF_INET; - route->dst_prefixlen = 16; - route->scope = RT_SCOPE_LINK; - route->priority = IPV4LL_ROUTE_METRIC; - route->protocol = RTPROT_STATIC; - } - if (network->n_dns > 0 || !strv_isempty(network->ntp) || - !strv_isempty(network->search_domains) || - !strv_isempty(network->route_domains)) + !ordered_set_isempty(network->search_domains) || + !ordered_set_isempty(network->route_domains)) link_dirty(link); return 0; @@ -525,7 +667,7 @@ bool network_has_static_ipv6_addresses(Network *network) { return false; } -int config_parse_netdev(const char *unit, +int config_parse_stacked_netdev(const char *unit, const char *filename, unsigned line, const char *section, @@ -535,81 +677,43 @@ int config_parse_netdev(const char *unit, const char *rvalue, void *data, void *userdata) { - Network *network = userdata; - _cleanup_free_ char *kind_string = NULL; - char *p; - NetDev *netdev; - NetDevKind kind; + _cleanup_free_ char *name = NULL; + NetDevKind kind = ltype; + Hashmap **h = data; int r; assert(filename); assert(lvalue); assert(rvalue); assert(data); + assert(IN_SET(kind, + NETDEV_KIND_VLAN, NETDEV_KIND_MACVLAN, NETDEV_KIND_MACVTAP, + NETDEV_KIND_IPVLAN, NETDEV_KIND_VXLAN, NETDEV_KIND_L2TP, + _NETDEV_KIND_TUNNEL)); - kind_string = strdup(lvalue); - if (!kind_string) + if (!ifname_valid(rvalue)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Invalid netdev name in %s=, ignoring assignment: %s", lvalue, rvalue); + return 0; + } + + name = strdup(rvalue); + if (!name) return log_oom(); - /* the keys are CamelCase versions of the kind */ - for (p = kind_string; *p; p++) - *p = tolower(*p); + r = hashmap_ensure_allocated(h, &string_hash_ops); + if (r < 0) + return log_oom(); - kind = netdev_kind_from_string(kind_string); - if (kind == _NETDEV_KIND_INVALID) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Invalid NetDev kind: %s", lvalue); - return 0; - } - - r = netdev_get(network->manager, rvalue, &netdev); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s could not be found, ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - if (netdev->kind != kind) { - log_syntax(unit, LOG_ERR, filename, line, 0, "NetDev is not a %s, ignoring assignment: %s", lvalue, rvalue); - return 0; - } - - 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; - case NETDEV_KIND_VLAN: - case NETDEV_KIND_MACVLAN: - case NETDEV_KIND_MACVTAP: - 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); - return 0; - } - - break; - default: - assert_not_reached("Cannot parse NetDev"); - } - - netdev_ref(netdev); + r = hashmap_put(*h, name, INT_TO_PTR(kind)); + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, + "Cannot add NetDev '%s' to network, ignoring assignment: %m", name); + else if (r == 0) + log_syntax(unit, LOG_DEBUG, filename, line, r, + "NetDev '%s' specified twice, ignoring.", name); + else + name = NULL; return 0; } @@ -635,8 +739,8 @@ int config_parse_domains( assert(rvalue); if (isempty(rvalue)) { - n->search_domains = strv_free(n->search_domains); - n->route_domains = strv_free(n->route_domains); + n->search_domains = ordered_set_free_free(n->search_domains); + n->route_domains = ordered_set_free_free(n->route_domains); return 0; } @@ -648,7 +752,8 @@ int config_parse_domains( r = extract_first_word(&p, &w, NULL, 0); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract search or route domain, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract search or route domain, ignoring: %s", rvalue); break; } if (r == 0) @@ -658,91 +763,39 @@ int config_parse_domains( domain = is_route ? w + 1 : w; if (dns_name_is_root(domain) || streq(domain, "*")) { - /* If the root domain appears as is, or the special token "*" is found, we'll consider this as - * routing domain, unconditionally. */ + /* If the root domain appears as is, or the special token "*" is found, we'll + * consider this as routing domain, unconditionally. */ is_route = true; - domain = "."; /* make sure we don't allow empty strings, thus write the root domain as "." */ + domain = "."; /* make sure we don't allow empty strings, thus write the root + * domain as "." */ } else { 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); + log_syntax(unit, LOG_ERR, filename, line, r, + "'%s' is not a valid domain name, ignoring.", domain); continue; } domain = normalized; if (is_localhost(domain)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "'localhost' domain names may not be configure as search or route domains, ignoring assignment: %s", domain); + log_syntax(unit, LOG_ERR, filename, line, 0, + "'localhost' domain may not be configured as search or route domain, ignoring assignment: %s", + domain); continue; } } - if (is_route) - r = strv_extend(&n->route_domains, domain); - else - r = strv_extend(&n->search_domains, domain); + OrderedSet **set = is_route ? &n->route_domains : &n->search_domains; + r = ordered_set_ensure_allocated(set, &string_hash_ops); + if (r < 0) + return r; + + r = ordered_set_put_strdup(*set, domain); if (r < 0) return log_oom(); } - strv_uniq(n->route_domains); - strv_uniq(n->search_domains); - - return 0; -} - -int config_parse_tunnel(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; - NetDev *netdev; - int r; - - assert(filename); - assert(lvalue); - assert(rvalue); - assert(data); - - r = netdev_get(network->manager, rvalue, &netdev); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Tunnel is invalid, ignoring assignment: %s", rvalue); - return 0; - } - - if (!IN_SET(netdev->kind, - NETDEV_KIND_IPIP, - NETDEV_KIND_SIT, - NETDEV_KIND_GRE, - NETDEV_KIND_GRETAP, - NETDEV_KIND_IP6GRE, - NETDEV_KIND_IP6GRETAP, - NETDEV_KIND_VTI, - NETDEV_KIND_VTI6, - NETDEV_KIND_IP6TNL)) { - log_syntax(unit, LOG_ERR, filename, line, 0, - "NetDev is not a tunnel, ignoring assignment: %s", rvalue); - 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); - return 0; - } - - netdev_ref(netdev); - return 0; } @@ -812,9 +865,14 @@ int config_parse_dhcp( else if (streq(rvalue, "both")) s = ADDRESS_FAMILY_YES; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DHCP option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse DHCP option, ignoring: %s", rvalue); return 0; } + + log_syntax(unit, LOG_WARNING, filename, line, 0, + "DHCP=%s is deprecated, please use DHCP=%s instead.", + rvalue, address_family_boolean_to_string(s)); } *dhcp = s; @@ -828,7 +886,8 @@ static const char* const dhcp_client_identifier_table[_DHCP_CLIENT_ID_MAX] = { }; DEFINE_PRIVATE_STRING_TABLE_LOOKUP_FROM_STRING(dhcp_client_identifier, DHCPClientIdentifier); -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier, "Failed to parse client identifier type"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_client_identifier, dhcp_client_identifier, DHCPClientIdentifier, + "Failed to parse client identifier type"); int config_parse_ipv6token( const char* unit, @@ -853,18 +912,20 @@ int config_parse_ipv6token( r = in_addr_from_string(AF_INET6, rvalue, &buffer); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 token, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse IPv6 token, ignoring: %s", rvalue); return 0; } - r = in_addr_is_null(AF_INET6, &buffer); - if (r != 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "IPv6 token cannot be the ANY address, ignoring: %s", rvalue); + if (in_addr_is_null(AF_INET6, &buffer)) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "IPv6 token cannot be the ANY address, ignoring: %s", rvalue); return 0; } if ((buffer.in6.s6_addr32[0] | buffer.in6.s6_addr32[1]) != 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "IPv6 token cannot be longer than 64 bits, ignoring: %s", rvalue); return 0; } @@ -918,7 +979,8 @@ int config_parse_ipv6_privacy_extensions( if (streq(rvalue, "kernel")) s = _IPV6_PRIVACY_EXTENSIONS_INVALID; else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse IPv6 privacy extensions option, ignoring: %s", rvalue); return 0; } } @@ -954,17 +1016,20 @@ int config_parse_hostname( return r; if (!hostname_is_valid(hn, false)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Hostname is not valid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Hostname is not valid, ignoring assignment: %s", rvalue); return 0; } r = dns_name_is_valid(hn); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to check validity of hostname '%s', ignoring assignment: %m", rvalue); + 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); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Hostname is not a valid DNS domain name, ignoring assignment: %s", rvalue); return 0; } @@ -996,7 +1061,8 @@ int config_parse_timezone( return r; if (!timezone_is_valid(tz, LOG_ERR)) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Timezone is not valid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Timezone is not valid, ignoring assignment: %s", rvalue); return 0; } @@ -1031,14 +1097,16 @@ int config_parse_dhcp_server_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) break; if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse DNS server address, ignoring: %s", w); continue; } @@ -1081,7 +1149,8 @@ int config_parse_radv_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) @@ -1098,8 +1167,8 @@ int config_parse_radv_dns( n->router_dns = m; } else - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse DNS server address, ignoring: %s", w); - + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse DNS server address, ignoring: %s", w); } return 0; @@ -1132,7 +1201,8 @@ int config_parse_radv_search_domains( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) @@ -1140,18 +1210,20 @@ int config_parse_radv_search_domains( 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); + 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 { - r = strv_push(&n->router_search_domains, w); - if (r >= 0) - w = NULL; - } + } else if (r == 0) + /* transfer ownership to simplify subsequent operations */ + idna = TAKE_PTR(w); + + r = ordered_set_ensure_allocated(&n->router_search_domains, &string_hash_ops); + if (r < 0) + return r; + + r = ordered_set_consume(n->router_search_domains, TAKE_PTR(idna)); + if (r < 0) + return r; } return 0; @@ -1185,14 +1257,16 @@ int config_parse_dhcp_server_ntp( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract word, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract word, ignoring: %s", rvalue); return 0; } if (r == 0) return 0; if (inet_pton(AF_INET, w, &a) <= 0) { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse NTP server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, 0, + "Failed to parse NTP server address, ignoring: %s", w); continue; } @@ -1234,7 +1308,8 @@ int config_parse_dns( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Invalid syntax, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid syntax, ignoring: %s", rvalue); break; } if (r == 0) @@ -1242,7 +1317,8 @@ int config_parse_dns( r = in_addr_from_string_auto(w, &family, &a); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse dns server address, ignoring: %s", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse dns server address, ignoring: %s", w); continue; } @@ -1291,7 +1367,8 @@ int config_parse_dnssec_negative_trust_anchors( r = extract_first_word(&p, &w, NULL, 0); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract negative trust anchor domain, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract negative trust anchor domain, ignoring: %s", rvalue); break; } if (r == 0) @@ -1299,7 +1376,8 @@ int config_parse_dnssec_negative_trust_anchors( r = dns_name_is_valid(w); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name, ignoring.", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "%s is not a valid domain name, ignoring.", w); continue; } @@ -1348,7 +1426,8 @@ int config_parse_ntp( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to extract NTP server name, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to extract NTP server name, ignoring: %s", rvalue); break; } if (r == 0) @@ -1356,15 +1435,21 @@ int config_parse_ntp( r = dns_name_is_valid_or_address(w); if (r <= 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s is not a valid domain name or IP address, ignoring.", w); + log_syntax(unit, LOG_ERR, filename, line, r, + "%s is not a valid domain name or IP address, ignoring.", w); continue; } - r = strv_push(l, w); + if (strv_length(*l) > MAX_NTP_SERVERS) { + log_syntax(unit, LOG_WARNING, filename, line, 0, + "More than %u NTP servers specified, ignoring \"%s\" and any subsequent entries.", + MAX_NTP_SERVERS, w); + break; + } + + r = strv_consume(l, TAKE_PTR(w)); if (r < 0) return log_oom(); - - w = NULL; } return 0; @@ -1401,14 +1486,16 @@ int config_parse_dhcp_user_class( if (r == -ENOMEM) return log_oom(); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Failed to split user classes option, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to split user classes option, ignoring: %s", rvalue); break; } if (r == 0) break; if (strlen(w) > 255) { - log_syntax(unit, LOG_ERR, filename, line, r, "%s length is not in the range 1-255, ignoring.", w); + log_syntax(unit, LOG_ERR, filename, line, 0, + "%s length is not in the range 1-255, ignoring.", w); continue; } @@ -1422,16 +1509,18 @@ int config_parse_dhcp_user_class( return 0; } -int config_parse_dhcp_route_table(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_section_route_table( + 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 rt; int r; @@ -1444,17 +1533,23 @@ int config_parse_dhcp_route_table(const char *unit, r = safe_atou32(rvalue, &rt); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Unable to read RouteTable, ignoring assignment: %s", rvalue); + "Failed to parse RouteTable=%s, ignoring assignment: %m", rvalue); return 0; } - network->dhcp_route_table = rt; - network->dhcp_route_table_set = true; + if (streq_ptr(section, "DHCP")) { + network->dhcp_route_table = rt; + network->dhcp_route_table_set = true; + } else { /* section is IPv6AcceptRA */ + network->ipv6_accept_ra_route_table = rt; + network->ipv6_accept_ra_route_table_set = true; + } return 0; } -DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); +DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, + "Failed to parse DHCP use domains setting"); static const char* const dhcp_use_domains_table[_DHCP_USE_DOMAINS_MAX] = { [DHCP_USE_DOMAINS_NO] = "no", @@ -1505,3 +1600,46 @@ int config_parse_iaid(const char *unit, return 0; } + +int config_parse_required_for_online( + 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; + LinkOperationalState s; + bool required = true; + int r; + + if (isempty(rvalue)) { + network->required_for_online = true; + network->required_operstate_for_online = LINK_OPERSTATE_DEGRADED; + return 0; + } + + s = link_operstate_from_string(rvalue); + if (s < 0) { + r = parse_boolean(rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse %s= setting, ignoring assignment: %s", + lvalue, rvalue); + return 0; + } + + required = r; + s = LINK_OPERSTATE_DEGRADED; + } + + network->required_for_online = required; + network->required_operstate_for_online = s; + + return 0; +} diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index f6e62cdd7..852144da3 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -20,6 +20,7 @@ #include "networkd-route.h" #include "networkd-routing-policy-rule.h" #include "networkd-util.h" +#include "ordered-set.h" #include "resolve-util.h" #define DHCP_ROUTE_METRIC 1024 @@ -83,16 +84,6 @@ typedef enum RADVPrefixDelegation { _RADV_PREFIX_DELEGATION_INVALID = -1, } RADVPrefixDelegation; -typedef struct NetworkConfigSection { - unsigned line; - char filename[]; -} 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; struct Network { @@ -106,12 +97,7 @@ struct Network { char **match_driver; char **match_type; char **match_name; - - Condition *match_host; - Condition *match_virt; - Condition *match_kernel_cmdline; - Condition *match_kernel_version; - Condition *match_arch; + LIST_HEAD(Condition, conditions); char *description; @@ -119,6 +105,10 @@ struct Network { NetDev *bond; NetDev *vrf; Hashmap *stacked_netdevs; + char *bridge_name; + char *bond_name; + char *vrf_name; + Hashmap *stacked_netdev_names; /* DHCP Client Support */ AddressFamilyBoolean dhcp; @@ -173,7 +163,7 @@ struct Network { usec_t router_dns_lifetime_usec; struct in6_addr *router_dns; unsigned n_router_dns; - char **router_search_domains; + OrderedSet *router_search_domains; bool dhcp6_force_pd_other_information; /* Start DHCPv6 PD also when 'O' RA flag is set, see RFC 7084, WPD-4 */ @@ -184,7 +174,10 @@ struct Network { int fast_leave; int allow_port_to_be_root; int unicast_flood; + int multicast_flood; int multicast_to_unicast; + int neighbor_suppression; + int learning; uint32_t cost; uint16_t priority; @@ -197,6 +190,7 @@ struct Network { size_t can_bitrate; unsigned can_sample_point; usec_t can_restart_us; + int can_triple_sampling; AddressFamilyBoolean ip_forward; bool ip_masquerade; @@ -209,27 +203,33 @@ struct Network { uint32_t ipv6_mtu; bool ipv6_accept_ra_use_dns; + bool ipv6_accept_ra_use_autonomous_prefix; + bool ipv6_accept_ra_use_onlink_prefix; bool active_slave; bool primary_slave; DHCPUseDomains ipv6_accept_ra_use_domains; uint32_t ipv6_accept_ra_route_table; + bool ipv6_accept_ra_route_table_set; union in_addr_union ipv6_token; IPv6PrivacyExtensions ipv6_privacy_extensions; struct ether_addr *mac; uint32_t mtu; + bool mtu_is_set; /* Indicate MTUBytes= is specified. */ int arp; int multicast; int allmulticast; bool unmanaged; bool configure_without_carrier; + bool ignore_carrier_loss; uint32_t iaid; DUID duid; bool iaid_set; bool required_for_online; /* Is this network required to be considered online? */ + LinkOperationalState required_operstate_for_online; LLDPMode lldp_mode; /* LLDP reception */ LLDPEmit lldp_emit; /* LLDP transmission */ @@ -263,7 +263,8 @@ struct Network { /* All kinds of DNS configuration */ struct in_addr_data *dns; unsigned n_dns; - char **search_domains, **route_domains; + OrderedSet *search_domains, *route_domains; + int dns_default_route; ResolveSupport llmnr; ResolveSupport mdns; @@ -283,6 +284,7 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(Network*, network_free); int network_load(Manager *manager); int network_load_one(Manager *manager, const char *filename); +int network_verify(Network *network); int network_get_by_name(Manager *manager, const char *name, Network **ret); int network_get(Manager *manager, sd_device *device, const char *ifname, const struct ether_addr *mac, Network **ret); @@ -291,7 +293,7 @@ void network_apply_anonymize_if_set(Network *network); bool network_has_static_ipv6_addresses(Network *network); -CONFIG_PARSER_PROTOTYPE(config_parse_netdev); +CONFIG_PARSER_PROTOTYPE(config_parse_stacked_netdev); CONFIG_PARSER_PROTOTYPE(config_parse_domains); CONFIG_PARSER_PROTOTYPE(config_parse_tunnel); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp); @@ -308,10 +310,11 @@ CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp); CONFIG_PARSER_PROTOTYPE(config_parse_dnssec_negative_trust_anchors); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_use_domains); CONFIG_PARSER_PROTOTYPE(config_parse_lldp_mode); -CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_route_table); +CONFIG_PARSER_PROTOTYPE(config_parse_section_route_table); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_user_class); CONFIG_PARSER_PROTOTYPE(config_parse_ntp); CONFIG_PARSER_PROTOTYPE(config_parse_iaid); +CONFIG_PARSER_PROTOTYPE(config_parse_required_for_online); /* Legacy IPv4LL support */ CONFIG_PARSER_PROTOTYPE(config_parse_ipv4ll); diff --git a/src/network/networkd-radv.c b/src/network/networkd-radv.c index 92cead052..8cb14b588 100644 --- a/src/network/networkd-radv.c +++ b/src/network/networkd-radv.c @@ -124,8 +124,8 @@ int prefix_new(Prefix **ret) { return 0; } -int prefix_new_static(Network *network, const char *filename, - unsigned section_line, Prefix **ret) { +static int prefix_new_static(Network *network, const char *filename, + unsigned section_line, Prefix **ret) { _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(prefix_freep) Prefix *prefix = NULL; int r; @@ -186,7 +186,7 @@ int config_parse_prefix(const char *unit, void *userdata) { Network *network = userdata; - _cleanup_(prefix_freep) Prefix *p = NULL; + _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; uint8_t prefixlen = 64; union in_addr_union in6addr; int r; @@ -228,7 +228,7 @@ int config_parse_prefix_flags(const char *unit, void *data, void *userdata) { Network *network = userdata; - _cleanup_(prefix_freep) Prefix *p = NULL; + _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; int r, val; assert(filename); @@ -272,7 +272,7 @@ int config_parse_prefix_lifetime(const char *unit, void *data, void *userdata) { Network *network = userdata; - _cleanup_(prefix_freep) Prefix *p = NULL; + _cleanup_(prefix_free_or_set_invalidp) Prefix *p = NULL; usec_t usec; int r; @@ -391,8 +391,9 @@ static int radv_set_dns(Link *link, Link *uplink) { } static int radv_set_domains(Link *link, Link *uplink) { - char **search_domains; + OrderedSet *search_domains; usec_t lifetime_usec; + _cleanup_free_ char **s = NULL; /* just free() because the strings are owned by the set */ if (!link->network->router_emit_domains) return 0; @@ -423,9 +424,13 @@ static int radv_set_domains(Link *link, Link *uplink) { return 0; set_domains: + s = ordered_set_get_strv(search_domains); + if (!s) + return log_oom(); + return sd_radv_set_dnssl(link->radv, DIV_ROUND_UP(lifetime_usec, USEC_PER_SEC), - search_domains); + s); } diff --git a/src/network/networkd-radv.h b/src/network/networkd-radv.h index bb88f8a40..3192bb8b6 100644 --- a/src/network/networkd-radv.h +++ b/src/network/networkd-radv.h @@ -8,6 +8,7 @@ #include "conf-parser.h" #include "networkd-address.h" #include "networkd-link.h" +#include "networkd-util.h" typedef struct Prefix Prefix; @@ -22,9 +23,8 @@ struct Prefix { int prefix_new(Prefix **ret); void prefix_free(Prefix *prefix); -int prefix_new_static(Network *network, const char *filename, unsigned section, Prefix **ret); -DEFINE_TRIVIAL_CLEANUP_FUNC(Prefix*, prefix_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(Prefix, prefix_free); CONFIG_PARSER_PROTOTYPE(config_parse_router_prefix_delegation); CONFIG_PARSER_PROTOTYPE(config_parse_router_preference); diff --git a/src/network/networkd-route.c b/src/network/networkd-route.c index 5553a7e3b..379077cbf 100644 --- a/src/network/networkd-route.c +++ b/src/network/networkd-route.c @@ -59,6 +59,7 @@ int route_new(Route **ret) { .table = RT_TABLE_MAIN, .lifetime = USEC_INFINITY, .quickack = -1, + .gateway_onlink = -1, }; *ret = TAKE_PTR(route); @@ -66,7 +67,7 @@ int route_new(Route **ret) { return 0; } -int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) { +static int route_new_static(Network *network, const char *filename, unsigned section_line, Route **ret) { _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL; _cleanup_(route_freep) Route *route = NULL; int r; @@ -406,71 +407,59 @@ int route_remove(Route *route, Link *link, RTM_DELROUTE, route->family, route->protocol); if (r < 0) - return log_error_errno(r, "Could not create RTM_DELROUTE message: %m"); + return log_link_error_errno(link, r, "Could not create RTM_DELROUTE message: %m"); - if (!in_addr_is_null(route->family, &route->gw)) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); + if (in_addr_is_null(route->family, &route->gw) == 0) { + r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw); if (r < 0) - return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m"); } if (route->dst_prefixlen) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6); + r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst); if (r < 0) - return log_error_errno(r, "Could not append RTA_DST attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) - return log_error_errno(r, "Could not set destination prefix length: %m"); + return log_link_error_errno(link, r, "Could not set destination prefix length: %m"); } if (route->src_prefixlen) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); + r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src); if (r < 0) - return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) - return log_error_errno(r, "Could not set source prefix length: %m"); + return log_link_error_errno(link, r, "Could not set source prefix length: %m"); } - if (!in_addr_is_null(route->family, &route->prefsrc)) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6); + if (in_addr_is_null(route->family, &route->prefsrc) == 0) { + r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc); if (r < 0) - return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); if (r < 0) - return log_error_errno(r, "Could not set scope: %m"); + return log_link_error_errno(link, r, "Could not set scope: %m"); r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) - return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m"); 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"); + return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m"); } 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"); + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -511,148 +500,158 @@ int route_configure( 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()) - return -E2BIG; + return log_link_error_errno(link, SYNTHETIC_ERRNO(E2BIG), + "Too many routes are configured, refusing: %m"); + + if (DEBUG_LOGGING) { + _cleanup_free_ char *dst = NULL, *dst_prefixlen = NULL, *src = NULL, *gw = NULL, *prefsrc = NULL; + + if (!in_addr_is_null(route->family, &route->dst)) { + (void) in_addr_to_string(route->family, &route->dst, &dst); + (void) asprintf(&dst_prefixlen, "/%u", route->dst_prefixlen); + } + if (!in_addr_is_null(route->family, &route->src)) + (void) in_addr_to_string(route->family, &route->src, &src); + if (!in_addr_is_null(route->family, &route->gw)) + (void) in_addr_to_string(route->family, &route->gw, &gw); + if (!in_addr_is_null(route->family, &route->prefsrc)) + (void) in_addr_to_string(route->family, &route->prefsrc, &prefsrc); + + log_link_debug(link, "Configuring route: dst: %s%s, src: %s, gw: %s, prefsrc: %s", + strna(dst), strempty(dst_prefixlen), strna(src), strna(gw), strna(prefsrc)); + } r = sd_rtnl_message_new_route(link->manager->rtnl, &req, RTM_NEWROUTE, route->family, route->protocol); if (r < 0) - return log_error_errno(r, "Could not create RTM_NEWROUTE message: %m"); + return log_link_error_errno(link, r, "Could not create RTM_NEWROUTE message: %m"); - if (!in_addr_is_null(route->family, &route->gw)) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_GATEWAY, &route->gw.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_GATEWAY, &route->gw.in6); + if (in_addr_is_null(route->family, &route->gw) == 0) { + r = netlink_message_append_in_addr_union(req, RTA_GATEWAY, route->family, &route->gw); if (r < 0) - return log_error_errno(r, "Could not append RTA_GATEWAY attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_GATEWAY attribute: %m"); r = sd_rtnl_message_route_set_family(req, route->family); if (r < 0) - return log_error_errno(r, "Could not set route family: %m"); + return log_link_error_errno(link, r, "Could not set route family: %m"); } if (route->dst_prefixlen) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_DST, &route->dst.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_DST, &route->dst.in6); + r = netlink_message_append_in_addr_union(req, RTA_DST, route->family, &route->dst); if (r < 0) - return log_error_errno(r, "Could not append RTA_DST attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_DST attribute: %m"); r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen); if (r < 0) - return log_error_errno(r, "Could not set destination prefix length: %m"); + return log_link_error_errno(link, r, "Could not set destination prefix length: %m"); } if (route->src_prefixlen) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_SRC, &route->src.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_SRC, &route->src.in6); + r = netlink_message_append_in_addr_union(req, RTA_SRC, route->family, &route->src); if (r < 0) - return log_error_errno(r, "Could not append RTA_SRC attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_SRC attribute: %m"); r = sd_rtnl_message_route_set_src_prefixlen(req, route->src_prefixlen); if (r < 0) - return log_error_errno(r, "Could not set source prefix length: %m"); + return log_link_error_errno(link, r, "Could not set source prefix length: %m"); } - if (!in_addr_is_null(route->family, &route->prefsrc)) { - if (route->family == AF_INET) - r = sd_netlink_message_append_in_addr(req, RTA_PREFSRC, &route->prefsrc.in); - else if (route->family == AF_INET6) - r = sd_netlink_message_append_in6_addr(req, RTA_PREFSRC, &route->prefsrc.in6); + if (in_addr_is_null(route->family, &route->prefsrc) == 0) { + r = netlink_message_append_in_addr_union(req, RTA_PREFSRC, route->family, &route->prefsrc); if (r < 0) - return log_error_errno(r, "Could not append RTA_PREFSRC attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_PREFSRC attribute: %m"); } r = sd_rtnl_message_route_set_scope(req, route->scope); if (r < 0) - return log_error_errno(r, "Could not set scope: %m"); + return log_link_error_errno(link, r, "Could not set scope: %m"); + + if (route->gateway_onlink >= 0) + SET_FLAG(route->flags, RTNH_F_ONLINK, route->gateway_onlink); r = sd_rtnl_message_route_set_flags(req, route->flags); if (r < 0) - return log_error_errno(r, "Could not set flags: %m"); + return log_link_error_errno(link, r, "Could not set flags: %m"); if (route->table != RT_TABLE_MAIN) { if (route->table < 256) { r = sd_rtnl_message_route_set_table(req, route->table); if (r < 0) - return log_error_errno(r, "Could not set route table: %m"); + return log_link_error_errno(link, r, "Could not set route table: %m"); } else { r = sd_rtnl_message_route_set_table(req, RT_TABLE_UNSPEC); if (r < 0) - return log_error_errno(r, "Could not set route table: %m"); + return log_link_error_errno(link, r, "Could not set route table: %m"); /* Table attribute to allow more than 256. */ r = sd_netlink_message_append_data(req, RTA_TABLE, &route->table, sizeof(route->table)); if (r < 0) - return log_error_errno(r, "Could not append RTA_TABLE attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_TABLE attribute: %m"); } } r = sd_netlink_message_append_u32(req, RTA_PRIORITY, route->priority); if (r < 0) - return log_error_errno(r, "Could not append RTA_PRIORITY attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_PRIORITY attribute: %m"); r = sd_netlink_message_append_u8(req, RTA_PREF, route->pref); if (r < 0) - return log_error_errno(r, "Could not append RTA_PREF attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_PREF attribute: %m"); if (route->lifetime != USEC_INFINITY && kernel_route_expiration_supported()) { r = sd_netlink_message_append_u32(req, RTA_EXPIRES, DIV_ROUND_UP(usec_sub_unsigned(route->lifetime, now(clock_boottime_or_monotonic())), USEC_PER_SEC)); if (r < 0) - return log_error_errno(r, "Could not append RTA_EXPIRES attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_EXPIRES attribute: %m"); } r = sd_rtnl_message_route_set_type(req, route->type); if (r < 0) - return log_error_errno(r, "Could not set route type: %m"); + return log_link_error_errno(link, r, "Could not set route type: %m"); 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"); + return log_link_error_errno(link, r, "Could not append RTA_OIF attribute: %m"); } r = sd_netlink_message_open_container(req, RTA_METRICS); if (r < 0) - return log_error_errno(r, "Could not append RTA_METRICS attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m"); if (route->mtu > 0) { r = sd_netlink_message_append_u32(req, RTAX_MTU, route->mtu); if (r < 0) - return log_error_errno(r, "Could not append RTAX_MTU attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTAX_MTU attribute: %m"); } if (route->initcwnd > 0) { r = sd_netlink_message_append_u32(req, RTAX_INITCWND, route->initcwnd); if (r < 0) - return log_error_errno(r, "Could not append RTAX_INITCWND attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTAX_INITCWND attribute: %m"); } if (route->initrwnd > 0) { r = sd_netlink_message_append_u32(req, RTAX_INITRWND, route->initrwnd); if (r < 0) - return log_error_errno(r, "Could not append RTAX_INITRWND attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTAX_INITRWND attribute: %m"); } if (route->quickack != -1) { r = sd_netlink_message_append_u32(req, RTAX_QUICKACK, route->quickack); if (r < 0) - return log_error_errno(r, "Could not append RTAX_QUICKACK attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTAX_QUICKACK attribute: %m"); } r = sd_netlink_message_close_container(req); if (r < 0) - return log_error_errno(r, "Could not append RTA_METRICS attribute: %m"); + return log_link_error_errno(link, r, "Could not append RTA_METRICS attribute: %m"); 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"); + return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); link_ref(link); @@ -660,7 +659,7 @@ int route_configure( r = route_add(link, route->family, &route->dst, route->dst_prefixlen, route->tos, route->priority, route->table, &route); if (r < 0) - return log_error_errno(r, "Could not add route: %m"); + return log_link_error_errno(link, r, "Could not add route: %m"); /* TODO: drop expiration handling once it can be pushed into the kernel */ route->lifetime = lifetime; @@ -669,7 +668,7 @@ int route_configure( r = sd_event_add_time(link->manager->event, &expire, clock_boottime_or_monotonic(), route->lifetime, 0, route_expire_handler, route); if (r < 0) - return log_error_errno(r, "Could not arm expiration timer: %m"); + return log_link_error_errno(link, r, "Could not arm expiration timer: %m"); } sd_event_source_unref(route->expire); @@ -678,6 +677,34 @@ int route_configure( return 0; } +int network_add_ipv4ll_route(Network *network) { + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; + int r; + + assert(network); + + if (!network->ipv4ll_route) + return 0; + + /* IPv4LLRoute= is in [Network] section. */ + r = route_new_static(network, NULL, 0, &n); + if (r < 0) + return r; + + r = in_addr_from_string(AF_INET, "169.254.0.0", &n->dst); + if (r < 0) + return r; + + n->family = AF_INET; + n->dst_prefixlen = 16; + n->scope = RT_SCOPE_LINK; + n->priority = IPV4LL_ROUTE_METRIC; + n->protocol = RTPROT_STATIC; + + TAKE_PTR(n); + return 0; +} + int config_parse_gateway( const char *unit, const char *filename, @@ -691,7 +718,7 @@ int config_parse_gateway( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; assert(filename); @@ -706,18 +733,20 @@ int config_parse_gateway( r = route_new_static(network, NULL, 0, &n); } else r = route_new_static(network, filename, section_line, &n); - if (r < 0) return r; - r = in_addr_from_string_auto(rvalue, &n->family, &n->gw); + if (n->family == AF_UNSPEC) + r = in_addr_from_string_auto(rvalue, &n->family, &n->gw); + else + r = in_addr_from_string(n->family, rvalue, &n->gw); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Route is invalid, ignoring assignment: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } TAKE_PTR(n); - return 0; } @@ -734,7 +763,7 @@ int config_parse_preferred_src( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; assert(filename); @@ -747,15 +776,17 @@ int config_parse_preferred_src( if (r < 0) return r; - r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc); + if (n->family == AF_UNSPEC) + r = in_addr_from_string_auto(rvalue, &n->family, &n->prefsrc); + else + r = in_addr_from_string(n->family, rvalue, &n->prefsrc); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Preferred source is invalid, ignoring assignment: %s", rvalue); + "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } TAKE_PTR(n); - return 0; } @@ -772,7 +803,7 @@ int config_parse_destination( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; union in_addr_union *buffer; unsigned char *prefixlen; int r; @@ -796,11 +827,13 @@ int config_parse_destination( } else assert_not_reached(lvalue); - r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen); + if (n->family == AF_UNSPEC) + r = in_addr_prefix_from_string_auto(rvalue, &n->family, buffer, prefixlen); + else + r = in_addr_prefix_from_string(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); + log_syntax(unit, LOG_ERR, filename, line, EINVAL, + "Invalid %s='%s', ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -821,7 +854,7 @@ int config_parse_route_priority( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; assert(filename); @@ -858,7 +891,7 @@ int config_parse_route_scope( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; assert(filename); @@ -898,7 +931,7 @@ int config_parse_route_table( void *data, void *userdata) { - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; Network *network = userdata; int r; @@ -936,7 +969,7 @@ int config_parse_gateway_onlink( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; assert(filename); @@ -952,11 +985,12 @@ int config_parse_gateway_onlink( r = parse_boolean(rvalue); if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Could not parse gateway onlink \"%s\", ignoring assignment: %m", rvalue); + "Could not parse %s=\"%s\", ignoring assignment: %m", lvalue, rvalue); return 0; } - SET_FLAG(n->flags, RTNH_F_ONLINK, r); + n->gateway_onlink = r; + TAKE_PTR(n); return 0; } @@ -974,7 +1008,7 @@ int config_parse_ipv6_route_preference( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; r = route_new_static(network, filename, section_line, &n); @@ -1009,7 +1043,7 @@ int config_parse_route_protocol( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; r = route_new_static(network, filename, section_line, &n); @@ -1025,7 +1059,8 @@ int config_parse_route_protocol( else { r = safe_atou8(rvalue , &n->protocol); if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, r, "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue); + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route protocol \"%s\", ignoring assignment: %m", rvalue); return 0; } } @@ -1047,7 +1082,7 @@ int config_parse_route_type( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; r = route_new_static(network, filename, section_line, &n); @@ -1065,7 +1100,8 @@ int config_parse_route_type( 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); + log_syntax(unit, LOG_ERR, filename, line, r, + "Could not parse route type \"%s\", ignoring assignment: %m", rvalue); return 0; } @@ -1085,7 +1121,7 @@ int config_parse_tcp_window( void *data, void *userdata) { - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; Network *network = userdata; uint64_t k; int r; @@ -1101,9 +1137,14 @@ int config_parse_tcp_window( return r; r = parse_size(rvalue, 1024, &k); - if (r < 0 || k > UINT32_MAX) { + if (r < 0) { log_syntax(unit, LOG_ERR, filename, line, r, - "Could not parse TCP %s \"%s\" bytes, ignoring assignment: %m", rvalue, lvalue); + "Could not parse TCP %s \"%s\", ignoring assignment: %m", lvalue, rvalue); + return 0; + } + if (k > UINT32_MAX) { + log_syntax(unit, LOG_ERR, filename, line, 0, + "Specified TCP %s \"%s\" is too large, ignoring assignment: %m", lvalue, rvalue); return 0; } @@ -1111,10 +1152,8 @@ int config_parse_tcp_window( n->initcwnd = k; else if (streq(lvalue, "InitialAdvertisedReceiveWindow")) n->initrwnd = k; - else { - log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse TCP %s: %s", lvalue, rvalue); - return 0; - } + else + assert_not_reached("Invalid TCP window type."); TAKE_PTR(n); return 0; @@ -1132,7 +1171,7 @@ int config_parse_quickack( void *data, void *userdata) { - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; Network *network = userdata; int k, r; @@ -1148,7 +1187,8 @@ int config_parse_quickack( k = parse_boolean(rvalue); if (k < 0) { - log_syntax(unit, LOG_ERR, filename, line, k, "Failed to parse TCP quickack, ignoring: %s", rvalue); + log_syntax(unit, LOG_ERR, filename, line, k, + "Failed to parse TCP quickack, ignoring: %s", rvalue); return 0; } @@ -1170,7 +1210,7 @@ int config_parse_route_mtu( void *userdata) { Network *network = userdata; - _cleanup_(route_freep) Route *n = NULL; + _cleanup_(route_free_or_set_invalidp) Route *n = NULL; int r; assert(filename); @@ -1190,3 +1230,29 @@ int config_parse_route_mtu( TAKE_PTR(n); return 0; } + +int route_section_verify(Route *route, Network *network) { + if (section_is_invalid(route->section)) + return -EINVAL; + + if (route->family == AF_UNSPEC) { + assert(route->section); + + return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), + "%s: Route section without Gateway=, Destination=, Source=, " + "or PreferredSource= field configured. " + "Ignoring [Route] section from line %u.", + route->section->filename, route->section->line); + } + + if (network->n_static_addresses == 0 && + in_addr_is_null(route->family, &route->gw) == 0 && + route->gateway_onlink < 0) { + log_warning("%s: Gateway= without static address configured. " + "Enabling GatewayOnLink= option.", + network->filename); + route->gateway_onlink = true; + } + + return 0; +} diff --git a/src/network/networkd-route.h b/src/network/networkd-route.h index 4eddf5194..1e8320fdc 100644 --- a/src/network/networkd-route.h +++ b/src/network/networkd-route.h @@ -7,6 +7,7 @@ typedef struct Route Route; typedef struct NetworkConfigSection NetworkConfigSection; #include "networkd-network.h" +#include "networkd-util.h" struct Route { Network *network; @@ -30,6 +31,7 @@ struct Route { uint32_t initrwnd; unsigned char pref; unsigned flags; + int gateway_onlink; union in_addr_union gw; union in_addr_union dst; @@ -42,7 +44,6 @@ struct Route { LIST_FIELDS(Route, routes); }; -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, link_netlink_message_handler_t callback); @@ -55,8 +56,11 @@ void route_update(Route *route, const union in_addr_union *src, unsigned char sr bool route_equal(Route *r1, Route *r2); int route_expire_handler(sd_event_source *s, uint64_t usec, void *userdata); +int route_section_verify(Route *route, Network *network); -DEFINE_TRIVIAL_CLEANUP_FUNC(Route*, route_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(Route, route_free); + +int network_add_ipv4ll_route(Network *network); CONFIG_PARSER_PROTOTYPE(config_parse_gateway); CONFIG_PARSER_PROTOTYPE(config_parse_preferred_src); diff --git a/src/network/networkd-routing-policy-rule.c b/src/network/networkd-routing-policy-rule.c index 2dc78622c..f6253215e 100644 --- a/src/network/networkd-routing-policy-rule.c +++ b/src/network/networkd-routing-policy-rule.c @@ -369,12 +369,8 @@ int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *lin if (r < 0) return log_error_errno(r, "Could not allocate RTM_DELRULE message: %m"); - if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->from)) { - if (routing_policy_rule->family == AF_INET) - r = sd_netlink_message_append_in_addr(m, FRA_SRC, &routing_policy_rule->from.in); - else - r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &routing_policy_rule->from.in6); - + if (in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->from) == 0) { + r = netlink_message_append_in_addr_union(m, FRA_SRC, routing_policy_rule->family, &routing_policy_rule->from); if (r < 0) return log_error_errno(r, "Could not append FRA_SRC attribute: %m"); @@ -383,12 +379,8 @@ int routing_policy_rule_remove(RoutingPolicyRule *routing_policy_rule, Link *lin return log_error_errno(r, "Could not set source prefix length: %m"); } - if (!in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->to)) { - if (routing_policy_rule->family == AF_INET) - r = sd_netlink_message_append_in_addr(m, FRA_DST, &routing_policy_rule->to.in); - else - r = sd_netlink_message_append_in6_addr(m, FRA_DST, &routing_policy_rule->to.in6); - + if (in_addr_is_null(routing_policy_rule->family, &routing_policy_rule->to) == 0) { + r = netlink_message_append_in_addr_union(m, FRA_DST, routing_policy_rule->family, &routing_policy_rule->to); if (r < 0) return log_error_errno(r, "Could not append FRA_DST attribute: %m"); @@ -496,12 +488,8 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl if (r < 0) return log_error_errno(r, "Could not allocate RTM_NEWRULE message: %m"); - if (!in_addr_is_null(rule->family, &rule->from)) { - if (rule->family == AF_INET) - r = sd_netlink_message_append_in_addr(m, FRA_SRC, &rule->from.in); - else - r = sd_netlink_message_append_in6_addr(m, FRA_SRC, &rule->from.in6); - + if (in_addr_is_null(rule->family, &rule->from) == 0) { + r = netlink_message_append_in_addr_union(m, FRA_SRC, rule->family, &rule->from); if (r < 0) return log_error_errno(r, "Could not append FRA_SRC attribute: %m"); @@ -510,12 +498,8 @@ int routing_policy_rule_configure(RoutingPolicyRule *rule, Link *link, link_netl return log_error_errno(r, "Could not set source prefix length: %m"); } - if (!in_addr_is_null(rule->family, &rule->to)) { - if (rule->family == AF_INET) - r = sd_netlink_message_append_in_addr(m, FRA_DST, &rule->to.in); - else - r = sd_netlink_message_append_in6_addr(m, FRA_DST, &rule->to.in6); - + if (in_addr_is_null(rule->family, &rule->to) == 0) { + r = netlink_message_append_in_addr_union(m, FRA_DST, rule->family, &rule->to); if (r < 0) return log_error_errno(r, "Could not append FRA_DST attribute: %m"); @@ -652,7 +636,7 @@ int config_parse_routing_policy_rule_tos( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -689,7 +673,7 @@ int config_parse_routing_policy_rule_priority( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -726,7 +710,7 @@ int config_parse_routing_policy_rule_table( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -763,7 +747,7 @@ int config_parse_routing_policy_rule_fwmark_mask( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -800,7 +784,7 @@ int config_parse_routing_policy_rule_prefix( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; union in_addr_union *buffer; uint8_t *prefixlen; @@ -847,7 +831,7 @@ int config_parse_routing_policy_rule_device( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -892,7 +876,7 @@ int config_parse_routing_policy_rule_port_range( const char *rvalue, void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; uint16_t low, high; int r; @@ -938,7 +922,7 @@ int config_parse_routing_policy_rule_ip_protocol( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -977,7 +961,7 @@ int config_parse_routing_policy_rule_invert( void *data, void *userdata) { - _cleanup_(routing_policy_rule_freep) RoutingPolicyRule *n = NULL; + _cleanup_(routing_policy_rule_free_or_set_invalidp) RoutingPolicyRule *n = NULL; Network *network = userdata; int r; @@ -1250,6 +1234,26 @@ int routing_policy_load_rules(const char *state_file, Set **rules) { return 0; } +static bool manager_links_have_routing_policy_rule(Manager *m, RoutingPolicyRule *rule) { + RoutingPolicyRule *link_rule; + Iterator i; + Link *link; + + assert(m); + assert(rule); + + HASHMAP_FOREACH(link, m->links, i) { + if (!link->network) + continue; + + LIST_FOREACH(rules, link_rule, link->network->rules) + if (routing_policy_rule_compare_func(link_rule, rule) == 0) + return true; + } + + return false; +} + void routing_policy_rule_purge(Manager *m, Link *link) { RoutingPolicyRule *rule, *existing; Iterator i; @@ -1260,15 +1264,24 @@ void routing_policy_rule_purge(Manager *m, Link *link) { SET_FOREACH(rule, m->rules_saved, i) { existing = set_get(m->rules_foreign, rule); - if (existing) { + if (!existing) + continue; /* Saved rule does not exist anymore. */ - r = routing_policy_rule_remove(rule, link, NULL); - if (r < 0) { - log_warning_errno(r, "Could not remove routing policy rules: %m"); - continue; - } + if (manager_links_have_routing_policy_rule(m, existing)) + continue; /* Existing links have the saved rule. */ - link->routing_policy_rule_remove_messages++; + /* Existing links do not have the saved rule. Let's drop the rule now, and re-configure it + * later when it is requested. */ + + r = routing_policy_rule_remove(existing, link, NULL); + if (r < 0) { + log_warning_errno(r, "Could not remove routing policy rules: %m"); + continue; } + + link->routing_policy_rule_remove_messages++; + + assert_se(set_remove(m->rules_foreign, existing) == existing); + routing_policy_rule_free(existing); } } diff --git a/src/network/networkd-routing-policy-rule.h b/src/network/networkd-routing-policy-rule.h index b35126e2c..e1bd78f80 100644 --- a/src/network/networkd-routing-policy-rule.h +++ b/src/network/networkd-routing-policy-rule.h @@ -13,6 +13,7 @@ typedef struct RoutingPolicyRule RoutingPolicyRule; #include "networkd-link.h" #include "networkd-network.h" +#include "networkd-util.h" typedef struct Network Network; typedef struct Link Link; @@ -54,7 +55,7 @@ struct RoutingPolicyRule { int routing_policy_rule_new(RoutingPolicyRule **ret); void routing_policy_rule_free(RoutingPolicyRule *rule); -DEFINE_TRIVIAL_CLEANUP_FUNC(RoutingPolicyRule*, routing_policy_rule_free); +DEFINE_NETWORK_SECTION_FUNCTIONS(RoutingPolicyRule, routing_policy_rule_free); 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); diff --git a/src/network/networkd-util.c b/src/network/networkd-util.c index 9b6bc1285..a392aadd4 100644 --- a/src/network/networkd-util.c +++ b/src/network/networkd-util.c @@ -102,3 +102,39 @@ int kernel_route_expiration_supported(void) { } return cached; } + +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 NetworkConfigSection *x, const NetworkConfigSection *y) { + int r; + + r = strcmp(x->filename, y->filename); + if (r != 0) + return r; + + return CMP(x->line, y->line); +} + +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; + + cs = malloc0(offsetof(NetworkConfigSection, filename) + strlen(filename) + 1); + if (!cs) + return -ENOMEM; + + strcpy(cs->filename, filename); + cs->line = line; + + *s = TAKE_PTR(cs); + + return 0; +} + +void network_config_section_free(NetworkConfigSection *cs) { + free(cs); +} diff --git a/src/network/networkd-util.h b/src/network/networkd-util.h index 435fdb7ca..a49e28935 100644 --- a/src/network/networkd-util.h +++ b/src/network/networkd-util.h @@ -2,18 +2,25 @@ #pragma once #include "conf-parser.h" +#include "hash-funcs.h" #include "macro.h" typedef enum AddressFamilyBoolean { /* This is a bitmask, though it usually doesn't feel that way! */ - ADDRESS_FAMILY_NO = 0, - ADDRESS_FAMILY_IPV4 = 1, - ADDRESS_FAMILY_IPV6 = 2, - ADDRESS_FAMILY_YES = 3, + ADDRESS_FAMILY_NO = 0, + ADDRESS_FAMILY_IPV4 = 1 << 0, + ADDRESS_FAMILY_IPV6 = 1 << 1, + ADDRESS_FAMILY_YES = ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_IPV6, _ADDRESS_FAMILY_BOOLEAN_MAX, _ADDRESS_FAMILY_BOOLEAN_INVALID = -1, } AddressFamilyBoolean; +typedef struct NetworkConfigSection { + unsigned line; + bool invalid; + char filename[]; +} NetworkConfigSection; + CONFIG_PARSER_PROTOTYPE(config_parse_address_family_boolean); CONFIG_PARSER_PROTOTYPE(config_parse_address_family_boolean_with_kernel); @@ -21,3 +28,29 @@ const char *address_family_boolean_to_string(AddressFamilyBoolean b) _const_; AddressFamilyBoolean address_family_boolean_from_string(const char *s) _const_; int kernel_route_expiration_supported(void); + +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; + +static inline bool section_is_invalid(NetworkConfigSection *section) { + /* If this retuns false, then it does _not_ mean the section is valid. */ + + if (!section) + return false; + + return section->invalid; +} + +#define DEFINE_NETWORK_SECTION_FUNCTIONS(type, free_func) \ + static inline void free_func##_or_set_invalid(type *p) { \ + assert(p); \ + \ + if (p->section) \ + p->section->invalid = true; \ + else \ + free_func(p); \ + } \ + DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func); \ + DEFINE_TRIVIAL_CLEANUP_FUNC(type*, free_func##_or_set_invalid); diff --git a/src/network/networkd.c b/src/network/networkd.c index fcecafe08..4b04aa046 100644 --- a/src/network/networkd.c +++ b/src/network/networkd.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "sd-daemon.h" #include "sd-event.h" diff --git a/src/network/test-networkd-conf.c b/src/network/test-networkd-conf.c index 86d4e7e73..6408719d1 100644 --- a/src/network/test-networkd-conf.c +++ b/src/network/test-networkd-conf.c @@ -172,7 +172,10 @@ static void test_config_parse_address_one(const char *rvalue, int family, unsign _cleanup_(network_freep) Network *network = NULL; assert_se(network = new0(Network, 1)); + assert_se(network->filename = strdup("hogehoge.network")); assert_se(config_parse_address("network", "filename", 1, "section", 1, "Address", 0, rvalue, network, network) == 0); + assert_se(network->n_static_addresses == 1); + assert_se(network_verify(network) >= 0); assert_se(network->n_static_addresses == n_addresses); if (n_addresses > 0) { assert_se(network->static_addresses); diff --git a/src/network/wait-online/link.c b/src/network/wait-online/link.c index 87d54fe65..a13373f7d 100644 --- a/src/network/wait-online/link.c +++ b/src/network/wait-online/link.c @@ -10,6 +10,7 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) { _cleanup_(link_freep) Link *l = NULL; + _cleanup_free_ char *n = NULL; int r; assert(m); @@ -23,30 +24,33 @@ int link_new(Manager *m, Link **ret, int ifindex, const char *ifname) { if (r < 0) return r; - l = new0(Link, 1); + n = strdup(ifname); + if (!n) + return -ENOMEM; + + l = new(Link, 1); if (!l) return -ENOMEM; - l->manager = m; - - l->ifname = strdup(ifname); - if (!l->ifname) - return -ENOMEM; + *l = (Link) { + .manager = m, + .ifname = TAKE_PTR(n), + .ifindex = ifindex, + .required_operstate = LINK_OPERSTATE_DEGRADED, + }; r = hashmap_put(m->links_by_name, l->ifname, l); if (r < 0) return r; - l->ifindex = ifindex; - r = hashmap_put(m->links, INT_TO_PTR(ifindex), l); if (r < 0) return r; if (ret) *ret = l; - l = NULL; + TAKE_PTR(l); return 0; } @@ -60,6 +64,7 @@ Link *link_free(Link *l) { hashmap_remove(l->manager->links_by_name, l->ifname); } + free(l->state); free(l->ifname); return mfree(l); } @@ -87,9 +92,8 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) { if (!new_ifname) return -ENOMEM; - hashmap_remove(l->manager->links_by_name, l->ifname); - free(l->ifname); - l->ifname = new_ifname; + assert_se(hashmap_remove(l->manager->links_by_name, l->ifname) == l); + free_and_replace(l->ifname, new_ifname); r = hashmap_put(l->manager->links_by_name, l->ifname, l); if (r < 0) @@ -100,17 +104,49 @@ int link_update_rtnl(Link *l, sd_netlink_message *m) { } int link_update_monitor(Link *l) { + _cleanup_free_ char *operstate = NULL, *required_operstate = NULL, *state = NULL; + LinkOperationalState s; + int r, ret = 0; + assert(l); + assert(l->ifname); - l->required_for_online = sd_network_link_get_required_for_online(l->ifindex) != 0; + r = sd_network_link_get_required_for_online(l->ifindex); + if (r < 0) + ret = log_link_debug_errno(l, r, "Failed to determine whether the link is required for online or not, " + "ignoring: %m"); + else + l->required_for_online = r > 0; - l->operational_state = mfree(l->operational_state); + r = sd_network_link_get_required_operstate_for_online(l->ifindex, &required_operstate); + if (r < 0) + ret = log_link_debug_errno(l, r, "Failed to get required operational state, ignoring: %m"); + else { + s = link_operstate_from_string(required_operstate); + if (s < 0) + ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL), + "Failed to parse required operational state, ignoring: %m"); + else + l->required_operstate = s; + } - sd_network_link_get_operational_state(l->ifindex, &l->operational_state); + r = sd_network_link_get_operational_state(l->ifindex, &operstate); + if (r < 0) + ret = log_link_debug_errno(l, r, "Failed to get operational state, ignoring: %m"); + else { + s = link_operstate_from_string(operstate); + if (s < 0) + ret = log_link_debug_errno(l, SYNTHETIC_ERRNO(EINVAL), + "Failed to parse operational state, ignoring: %m"); + else + l->operational_state = s; + } - l->state = mfree(l->state); + r = sd_network_link_get_setup_state(l->ifindex, &state); + if (r < 0) + ret = log_link_debug_errno(l, r, "Failed to get setup state, ignoring: %m"); + else + free_and_replace(l->state, state); - sd_network_link_get_setup_state(l->ifindex, &l->state); - - return 0; + return ret; } diff --git a/src/network/wait-online/link.h b/src/network/wait-online/link.h index 57e8f12e0..d58129dfe 100644 --- a/src/network/wait-online/link.h +++ b/src/network/wait-online/link.h @@ -3,6 +3,9 @@ #include "sd-netlink.h" +#include "log-link.h" +#include "network-util.h" + typedef struct Link Link; typedef struct Manager Manager; @@ -14,7 +17,8 @@ struct Link { unsigned flags; bool required_for_online; - char *operational_state; + LinkOperationalState required_operstate; + LinkOperationalState operational_state; char *state; }; diff --git a/src/network/wait-online/manager.c b/src/network/wait-online/manager.c index 67218b6db..e1f9a812f 100644 --- a/src/network/wait-online/manager.c +++ b/src/network/wait-online/manager.c @@ -13,7 +13,7 @@ #include "time-util.h" #include "util.h" -bool manager_ignore_link(Manager *m, Link *link) { +static bool manager_ignore_link(Manager *m, Link *link) { assert(m); assert(link); @@ -22,7 +22,7 @@ bool manager_ignore_link(Manager *m, Link *link) { return true; /* if interfaces are given on the command line, ignore all others */ - if (m->interfaces && !strv_contains(m->interfaces, link->ifname)) + if (m->interfaces && !hashmap_contains(m->interfaces, link->ifname)) return true; if (!link->required_for_online) @@ -32,45 +32,82 @@ bool manager_ignore_link(Manager *m, Link *link) { return strv_fnmatch(m->ignore, link->ifname, 0); } -bool manager_all_configured(Manager *m) { - Iterator i; - Link *l; - char **ifname; - bool one_ready = false; +static int manager_link_is_online(Manager *m, Link *l, LinkOperationalState s) { + /* This returns the following: + * -EAGAIN: not processed by udev or networkd + * 0: operstate is not enough + * 1: online */ - /* wait for all the links given on the command line to appear */ - STRV_FOREACH(ifname, m->interfaces) { - l = hashmap_get(m->links_by_name, *ifname); - if (!l) { - log_debug("still waiting for %s", *ifname); - return false; + if (!l->state) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN), + "link has not yet been processed by udev"); + + if (STR_IN_SET(l->state, "configuring", "pending")) + return log_link_debug_errno(l, SYNTHETIC_ERRNO(EAGAIN), + "link is being processed by networkd"); + + if (s < 0) + s = m->required_operstate >= 0 ? m->required_operstate : l->required_operstate; + + if (l->operational_state < s) { + log_link_debug(l, "Operational state '%s' is below '%s'", + link_operstate_to_string(l->operational_state), + link_operstate_to_string(s)); + return 0; + } + + return 1; +} + +bool manager_configured(Manager *m) { + bool one_ready = false; + Iterator i; + const char *ifname; + void *p; + Link *l; + int r; + + if (!hashmap_isempty(m->interfaces)) { + /* wait for all the links given on the command line to appear */ + HASHMAP_FOREACH_KEY(p, ifname, m->interfaces, i) { + LinkOperationalState s = PTR_TO_INT(p); + + l = hashmap_get(m->links_by_name, ifname); + if (!l) { + log_debug("still waiting for %s", ifname); + if (!m->any) + return false; + continue; + } + + if (manager_link_is_online(m, l, s) <= 0) { + if (!m->any) + return false; + continue; + } + + one_ready = true; } + + /* all interfaces given by the command line are online, or + * one of the specified interfaces is online. */ + return one_ready; } /* wait for all links networkd manages to be in admin state 'configured' - and at least one link to gain a carrier */ + * and at least one link to gain a carrier */ HASHMAP_FOREACH(l, m->links, i) { if (manager_ignore_link(m, l)) { - log_info("ignoring: %s", l->ifname); + log_link_info(l, "link is ignored"); continue; } - if (!l->state) { - log_debug("link %s has not yet been processed by udev", - l->ifname); + r = manager_link_is_online(m, l, _LINK_OPERSTATE_INVALID); + if (r < 0 && !m->any) return false; - } - - if (STR_IN_SET(l->state, "configuring", "pending")) { - log_debug("link %s is being processed by networkd", - l->ifname); - return false; - } - - if (l->operational_state && - STR_IN_SET(l->operational_state, "degraded", "routable")) + if (r > 0) /* we wait for at least one link to be ready, - regardless of who manages it */ + * regardless of who manages it */ one_ready = true; } @@ -120,21 +157,21 @@ static int manager_process_link(sd_netlink *rtnl, sd_netlink_message *mm, void * r = link_new(m, &l, ifindex, ifname); if (r < 0) return log_error_errno(r, "Failed to create link object: %m"); - - r = link_update_monitor(l); - if (r < 0) - return log_error_errno(r, "Failed to initialize link object: %m"); } r = link_update_rtnl(l, mm); if (r < 0) - return log_warning_errno(r, "Failed to process RTNL link message: %m");; + log_link_warning_errno(l, r, "Failed to process RTNL link message, ignoring: %m"); + + r = link_update_monitor(l); + if (r < 0) + log_link_warning_errno(l, r, "Failed to update link state, ignoring: %m"); break; case RTM_DELLINK: if (l) { - log_debug("Removing link %i", l->ifindex); + log_link_debug(l, "Removing link"); link_free(l); } @@ -152,7 +189,7 @@ static int on_rtnl_event(sd_netlink *rtnl, sd_netlink_message *mm, void *userdat if (r < 0) return r; - if (manager_all_configured(m)) + if (manager_configured(m)) sd_event_exit(m->event, 0); return 1; @@ -217,10 +254,10 @@ static int on_network_event(sd_event_source *s, int fd, uint32_t revents, void * HASHMAP_FOREACH(l, m->links, i) { r = link_update_monitor(l); if (r < 0) - log_warning_errno(r, "Failed to update monitor information for %i: %m", l->ifindex); + log_link_warning_errno(l, r, "Failed to update monitor information: %m"); } - if (manager_all_configured(m)) + if (manager_configured(m)) sd_event_exit(m->event, 0); return 0; @@ -251,18 +288,24 @@ static int manager_network_monitor_listen(Manager *m) { return 0; } -int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout) { +int manager_new(Manager **ret, Hashmap *interfaces, char **ignore, + LinkOperationalState required_operstate, + bool any, usec_t timeout) { _cleanup_(manager_freep) Manager *m = NULL; int r; assert(ret); - m = new0(Manager, 1); + m = new(Manager, 1); if (!m) return -ENOMEM; - m->interfaces = interfaces; - m->ignore = ignore; + *m = (Manager) { + .interfaces = interfaces, + .ignore = ignore, + .required_operstate = required_operstate, + .any = any, + }; r = sd_event_default(&m->event); if (r < 0) diff --git a/src/network/wait-online/manager.h b/src/network/wait-online/manager.h index 0fde272dd..dd7d847dd 100644 --- a/src/network/wait-online/manager.h +++ b/src/network/wait-online/manager.h @@ -6,6 +6,8 @@ #include "sd-network.h" #include "hashmap.h" +#include "network-util.h" +#include "time-util.h" typedef struct Manager Manager; typedef struct Link Link; @@ -14,9 +16,13 @@ struct Manager { Hashmap *links; Hashmap *links_by_name; - char **interfaces; + /* Do not free the two members below. */ + Hashmap *interfaces; char **ignore; + LinkOperationalState required_operstate; + bool any; + sd_netlink *rtnl; sd_event_source *rtnl_event_source; @@ -27,9 +33,10 @@ struct Manager { }; void manager_free(Manager *m); -int manager_new(Manager **ret, char **interfaces, char **ignore, usec_t timeout); +int manager_new(Manager **ret, Hashmap *interfaces, char **ignore, + LinkOperationalState required_operstate, + bool any, usec_t timeout); DEFINE_TRIVIAL_CLEANUP_FUNC(Manager*, manager_free); -bool manager_all_configured(Manager *m); -bool manager_ignore_link(Manager *m, Link *link); +bool manager_configured(Manager *m); diff --git a/src/network/wait-online/wait-online.c b/src/network/wait-online/wait-online.c index 71b6cf6b8..4ce2ac31b 100644 --- a/src/network/wait-online/wait-online.c +++ b/src/network/wait-online/wait-online.c @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include +#include #include "sd-daemon.h" @@ -9,14 +11,17 @@ #include "manager.h" #include "pretty-print.h" #include "signal-util.h" +#include "socket-util.h" #include "strv.h" static bool arg_quiet = false; static usec_t arg_timeout = 120 * USEC_PER_SEC; -static char **arg_interfaces = NULL; +static Hashmap *arg_interfaces = NULL; static char **arg_ignore = NULL; +static LinkOperationalState arg_required_operstate = _LINK_OPERSTATE_INVALID; +static bool arg_any = false; -STATIC_DESTRUCTOR_REGISTER(arg_interfaces, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_interfaces, hashmap_free_free_keyp); STATIC_DESTRUCTOR_REGISTER(arg_ignore, strv_freep); static int help(void) { @@ -32,8 +37,12 @@ static int help(void) { " -h --help Show this help\n" " --version Print version string\n" " -q --quiet Do not show status information\n" - " -i --interface=INTERFACE Block until at least these interfaces have appeared\n" + " -i --interface=INTERFACE[:OPERSTATE]\n" + " Block until at least these interfaces have appeared\n" " --ignore=INTERFACE Don't take these interfaces into account\n" + " -o --operational-state=OPERSTATE\n" + " Required operational state\n" + " --any Wait until at least one of the interfaces is online\n" " --timeout=SECS Maximum time to wait for network connectivity\n" "\nSee the %s for details.\n" , program_invocation_short_name @@ -43,21 +52,70 @@ static int help(void) { return 0; } +static int parse_interface_with_operstate(const char *str) { + _cleanup_free_ char *ifname = NULL; + LinkOperationalState s; + const char *p; + int r; + + assert(str); + + p = strchr(str, ':'); + if (p) { + if (isempty(p + 1)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Operational state is empty."); + + s = link_operstate_from_string(p + 1); + if (s < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid operational state '%s'", p + 1); + + ifname = strndup(optarg, p - optarg); + } else { + s = _LINK_OPERSTATE_INVALID; + ifname = strdup(str); + } + if (!ifname) + return log_oom(); + + if (!ifname_valid(ifname)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid interface name '%s'", ifname); + + r = hashmap_ensure_allocated(&arg_interfaces, &string_hash_ops); + if (r < 0) + return log_oom(); + + r = hashmap_put(arg_interfaces, ifname, INT_TO_PTR(s)); + if (r < 0) + return log_error_errno(r, "Failed to store interface name: %m"); + if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Interface name %s is already specified", ifname); + + TAKE_PTR(ifname); + return 0; +} + static int parse_argv(int argc, char *argv[]) { enum { ARG_VERSION = 0x100, ARG_IGNORE, + ARG_ANY, ARG_TIMEOUT, }; static const struct option options[] = { - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, ARG_VERSION }, - { "quiet", no_argument, NULL, 'q' }, - { "interface", required_argument, NULL, 'i' }, - { "ignore", required_argument, NULL, ARG_IGNORE }, - { "timeout", required_argument, NULL, ARG_TIMEOUT }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, ARG_VERSION }, + { "quiet", no_argument, NULL, 'q' }, + { "interface", required_argument, NULL, 'i' }, + { "ignore", required_argument, NULL, ARG_IGNORE }, + { "operational-state", required_argument, NULL, 'o' }, + { "any", no_argument, NULL, ARG_ANY }, + { "timeout", required_argument, NULL, ARG_TIMEOUT }, {} }; @@ -66,7 +124,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hi:q", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hi:qo:", options, NULL)) >= 0) switch (c) { @@ -82,9 +140,9 @@ static int parse_argv(int argc, char *argv[]) { return version(); case 'i': - if (strv_extend(&arg_interfaces, optarg) < 0) - return log_oom(); - + r = parse_interface_with_operstate(optarg); + if (r < 0) + return r; break; case ARG_IGNORE: @@ -93,11 +151,25 @@ static int parse_argv(int argc, char *argv[]) { break; + case 'o': { + LinkOperationalState s; + + s = link_operstate_from_string(optarg); + if (s < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid operational state '%s'", optarg); + + arg_required_operstate = s; + break; + } + case ARG_ANY: + arg_any = true; + break; + case ARG_TIMEOUT: r = parse_sec(optarg, &arg_timeout); if (r < 0) return r; - break; case '?': @@ -124,15 +196,15 @@ static int run(int argc, char *argv[]) { return r; if (arg_quiet) - log_set_max_level(LOG_WARNING); + log_set_max_level(LOG_ERR); assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGTERM, SIGINT, -1) >= 0); - r = manager_new(&m, arg_interfaces, arg_ignore, arg_timeout); + r = manager_new(&m, arg_interfaces, arg_ignore, arg_required_operstate, arg_any, arg_timeout); if (r < 0) return log_error_errno(r, "Could not create manager: %m"); - if (manager_all_configured(m)) + if (manager_configured(m)) goto success; notify_message = notify_start("READY=1\n" diff --git a/src/nspawn/meson.build b/src/nspawn/meson.build index be54ba36c..31217c7b4 100644 --- a/src/nspawn/meson.build +++ b/src/nspawn/meson.build @@ -10,6 +10,8 @@ libnspawn_core_sources = files(''' nspawn-mount.h nspawn-network.c nspawn-network.h + nspawn-oci.c + nspawn-oci.h nspawn-patch-uid.c nspawn-patch-uid.h nspawn-register.c diff --git a/src/nspawn/nspawn-cgroup.c b/src/nspawn/nspawn-cgroup.c index 97fa092ca..168125d31 100644 --- a/src/nspawn/nspawn-cgroup.c +++ b/src/nspawn/nspawn-cgroup.c @@ -5,6 +5,7 @@ #include "alloc-util.h" #include "fd-util.h" #include "fileio.h" +#include "format-util.h" #include "fs-util.h" #include "mkdir.h" #include "mount-util.h" @@ -263,7 +264,7 @@ static int mount_legacy_cgroup_hierarchy( if (r > 0) return 0; - mkdir_p(to, 0755); + (void) mkdir_p(to, 0755); /* The superblock mount options of the mount point need to be * identical to the hosts', and hence writable... */ diff --git a/src/nspawn/nspawn-gperf.gperf b/src/nspawn/nspawn-gperf.gperf index dec53a06f..79304d21a 100644 --- a/src/nspawn/nspawn-gperf.gperf +++ b/src/nspawn/nspawn-gperf.gperf @@ -62,6 +62,7 @@ Files.Volatile, config_parse_volatile_mode, 0, of Files.Bind, config_parse_bind, 0, 0 Files.BindReadOnly, config_parse_bind, 1, 0 Files.TemporaryFileSystem, config_parse_tmpfs, 0, 0 +Files.Inaccessible, config_parse_inaccessible, 0, 0 Files.Overlay, config_parse_overlay, 0, 0 Files.OverlayReadOnly, config_parse_overlay, 1, 0 Files.PrivateUsersChown, config_parse_tristate, 0, offsetof(Settings, userns_chown) diff --git a/src/nspawn/nspawn-mount.c b/src/nspawn/nspawn-mount.c index a9af88974..5a1bce4ab 100644 --- a/src/nspawn/nspawn-mount.c +++ b/src/nspawn/nspawn-mount.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "escape.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "label.h" #include "mkdir.h" @@ -16,12 +17,12 @@ #include "path-util.h" #include "rm-rf.h" #include "set.h" +#include "sort-util.h" #include "stat-util.h" #include "string-util.h" #include "strv.h" #include "tmpfile-util.h" #include "user-util.h" -#include "util.h" CustomMount* custom_mount_add(CustomMount **l, size_t *n, CustomMountType t) { CustomMount *c, *ret; @@ -65,6 +66,7 @@ void custom_mount_free_all(CustomMount *l, size_t n) { } strv_free(m->lower); + free(m->type_argument); } free(l); @@ -116,32 +118,40 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n) { for (i = 0; i < n; i++) { CustomMount *m = l + i; - if (m->source) { - char *s; + /* /proc we mount in the inner child, i.e. when we acquired CLONE_NEWPID. All other mounts we mount + * already in the outer child, so that the mounts are already established before CLONE_NEWPID and in + * particular CLONE_NEWUSER. This also means any custom mounts below /proc also need to be mounted in + * the inner child, not the outer one. Determine this here. */ + m->in_userns = path_startswith(m->destination, "/proc"); - s = resolve_source_path(dest, m->source); - if (!s) - return log_oom(); + if (m->type == CUSTOM_MOUNT_BIND) { + if (m->source) { + char *s; - free_and_replace(m->source, s); - } else { - /* No source specified? In that case, use a throw-away temporary directory in /var/tmp */ + s = resolve_source_path(dest, m->source); + if (!s) + return log_oom(); - m->rm_rf_tmpdir = strdup("/var/tmp/nspawn-temp-XXXXXX"); - if (!m->rm_rf_tmpdir) - return log_oom(); + free_and_replace(m->source, s); + } else { + /* No source specified? In that case, use a throw-away temporary directory in /var/tmp */ - if (!mkdtemp(m->rm_rf_tmpdir)) { - m->rm_rf_tmpdir = mfree(m->rm_rf_tmpdir); - return log_error_errno(errno, "Failed to acquire temporary directory: %m"); + m->rm_rf_tmpdir = strdup("/var/tmp/nspawn-temp-XXXXXX"); + if (!m->rm_rf_tmpdir) + return log_oom(); + + if (!mkdtemp(m->rm_rf_tmpdir)) { + m->rm_rf_tmpdir = mfree(m->rm_rf_tmpdir); + return log_error_errno(errno, "Failed to acquire temporary directory: %m"); + } + + m->source = strjoin(m->rm_rf_tmpdir, "/src"); + if (!m->source) + return log_oom(); + + if (mkdir(m->source, 0755) < 0) + return log_error_errno(errno, "Failed to create %s: %m", m->source); } - - m->source = strjoin(m->rm_rf_tmpdir, "/src"); - if (!m->source) - return log_oom(); - - if (mkdir(m->source, 0755) < 0) - return log_error_errno(errno, "Failed to create %s: %m", m->source); } if (m->type == CUSTOM_MOUNT_OVERLAY) { @@ -206,23 +216,24 @@ int bind_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_only) } if (isempty(source)) - source = NULL; + source = mfree(source); else if (!source_path_is_valid(source)) return -EINVAL; if (!path_is_absolute(destination)) return -EINVAL; + if (empty_or_root(destination)) + return -EINVAL; m = custom_mount_add(l, n, CUSTOM_MOUNT_BIND); if (!m) return -ENOMEM; - m->source = source; - m->destination = destination; + m->source = TAKE_PTR(source); + m->destination = TAKE_PTR(destination); m->read_only = read_only; - m->options = opts; + m->options = TAKE_PTR(opts); - source = destination = opts = NULL; return 0; } @@ -251,6 +262,8 @@ int tmpfs_mount_parse(CustomMount **l, size_t *n, const char *s) { if (!path_is_absolute(path)) return -EINVAL; + if (empty_or_root(path)) + return -EINVAL; m = custom_mount_add(l, n, CUSTOM_MOUNT_TMPFS); if (!m) @@ -302,7 +315,7 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl /* If the upper directory is unspecified, then let's create it automatically as a throw-away directory * in /var/tmp */ if (isempty(upper)) - upper = NULL; + upper = mfree(upper); else if (!source_path_is_valid(upper)) return -EINVAL; @@ -310,6 +323,9 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl return -EINVAL; } + if (empty_or_root(destination)) + return -EINVAL; + m = custom_mount_add(l, n, CUSTOM_MOUNT_OVERLAY); if (!m) return -ENOMEM; @@ -322,6 +338,29 @@ int overlay_mount_parse(CustomMount **l, size_t *n, const char *s, bool read_onl return 0; } +int inaccessible_mount_parse(CustomMount **l, size_t *n, const char *s) { + _cleanup_free_ char *path = NULL; + CustomMount *m; + + assert(l); + assert(n); + assert(s); + + if (!path_is_absolute(s)) + return -EINVAL; + + path = strdup(s); + if (!path) + return -ENOMEM; + + m = custom_mount_add(l, n, CUSTOM_MOUNT_INACCESSIBLE); + if (!m) + return -ENOMEM; + + m->destination = TAKE_PTR(path); + return 0; +} + int tmpfs_patch_options( const char *options, uid_t uid_shift, @@ -489,9 +528,9 @@ int mount_all(const char *dest, uid_t uid_shift, const char *selinux_apifs_context) { -#define PROC_INACCESSIBLE(path) \ - { NULL, (path), NULL, NULL, MS_BIND, \ - MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO|MOUNT_INACCESSIBLE_REG }, /* Bind mount first ... */ \ +#define PROC_INACCESSIBLE_REG(path) \ + { "/run/systemd/inaccessible/reg", (path), NULL, NULL, MS_BIND, \ + MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO }, /* Bind mount first ... */ \ { NULL, (path), NULL, NULL, MS_BIND|MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REMOUNT, \ MOUNT_IN_USERNS|MOUNT_APPLY_APIVFS_RO } /* Then, make it r/o */ @@ -526,11 +565,11 @@ int mount_all(const char *dest, /* Make these files inaccessible to container payloads: they potentially leak information about kernel * internals or the host's execution environment to the container */ - PROC_INACCESSIBLE("/proc/kallsyms"), - PROC_INACCESSIBLE("/proc/kcore"), - PROC_INACCESSIBLE("/proc/keys"), - PROC_INACCESSIBLE("/proc/sysrq-trigger"), - PROC_INACCESSIBLE("/proc/timer_list"), + PROC_INACCESSIBLE_REG("/proc/kallsyms"), + PROC_INACCESSIBLE_REG("/proc/kcore"), + PROC_INACCESSIBLE_REG("/proc/keys"), + PROC_INACCESSIBLE_REG("/proc/sysrq-trigger"), + PROC_INACCESSIBLE_REG("/proc/timer_list"), /* Make these directories read-only to container payloads: they show hardware information, and in some * cases contain tunables the container really shouldn't have access to. */ @@ -542,6 +581,9 @@ int mount_all(const char *dest, PROC_READ_ONLY("/proc/irq"), PROC_READ_ONLY("/proc/scsi"), + { "mqueue", "/dev/mqueue", "mqueue", NULL, MS_NOSUID|MS_NOEXEC|MS_NODEV, + MOUNT_IN_USERNS }, + /* 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|MOUNT_APPLY_TMPFS_TMP }, @@ -557,8 +599,6 @@ 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, @@ -568,7 +608,6 @@ int mount_all(const char *dest, #endif }; - _cleanup_(unlink_and_freep) char *inaccessible = NULL; bool use_userns = (mount_settings & MOUNT_USE_USERNS); bool netns = (mount_settings & MOUNT_APPLY_APIVFS_NETNS); bool ro = (mount_settings & MOUNT_APPLY_APIVFS_RO); @@ -579,7 +618,7 @@ int mount_all(const char *dest, for (k = 0; k < ELEMENTSOF(mount_table); k++) { _cleanup_free_ char *where = NULL, *options = NULL; - const char *o, *what; + const char *o; bool fatal = (mount_table[k].mount_settings & MOUNT_FATAL); if (in_userns != (bool)(mount_table[k].mount_settings & MOUNT_IN_USERNS)) @@ -598,33 +637,14 @@ int mount_all(const char *dest, if (r < 0) return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, mount_table[k].where); - if (mount_table[k].mount_settings & MOUNT_INACCESSIBLE_REG) { - - if (!inaccessible) { - _cleanup_free_ char *np = NULL; - - r = tempfn_random_child(NULL, "inaccessible", &np); - if (r < 0) - return log_error_errno(r, "Failed to generate inaccessible file node path: %m"); - - r = touch_file(np, false, USEC_INFINITY, UID_INVALID, GID_INVALID, 0000); - if (r < 0) - return log_error_errno(r, "Failed to create inaccessible file node '%s': %m", np); - - inaccessible = TAKE_PTR(np); - } - - what = inaccessible; - } else - what = mount_table[k].what; - - r = path_is_mount_point(where, NULL, 0); - if (r < 0 && r != -ENOENT) - return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); - /* Skip this entry if it is not a remount. */ - if (what && r > 0) - continue; + if (mount_table[k].what) { + r = path_is_mount_point(where, NULL, 0); + if (r < 0 && r != -ENOENT) + return log_error_errno(r, "Failed to detect whether %s is a mount point: %m", where); + if (r > 0) + continue; + } r = mkdir_userns_p(dest, where, 0755, (use_userns && !in_userns) ? uid_shift : UID_INVALID); if (r < 0 && r != -EEXIST) { @@ -649,7 +669,7 @@ int mount_all(const char *dest, } r = mount_verbose(fatal ? LOG_ERR : LOG_DEBUG, - what, + mount_table[k].what, where, mount_table[k].type, mount_table[k].flags, @@ -662,7 +682,6 @@ int mount_all(const char *dest, } static int mount_bind(const char *dest, CustomMount *m) { - _cleanup_free_ char *where = NULL; struct stat source_st, dest_st; int r; @@ -706,7 +725,6 @@ static int mount_bind(const char *dest, CustomMount *m) { r = touch(where); if (r < 0) return log_error_errno(r, "Failed to create mount point %s: %m", where); - } r = mount_verbose(LOG_ERR, m->source, where, NULL, MS_BIND | MS_REC, m->options); @@ -714,7 +732,7 @@ static int mount_bind(const char *dest, CustomMount *m) { return r; if (m->read_only) { - r = bind_remount_recursive(where, true, NULL); + r = bind_remount_recursive(where, MS_RDONLY, MS_RDONLY, NULL); if (r < 0) return log_error_errno(r, "Read-only bind mount failed: %m"); } @@ -768,7 +786,6 @@ static char *joined_and_escaped_lower_dirs(char **lower) { } static int mount_overlay(const char *dest, CustomMount *m) { - _cleanup_free_ char *lower = NULL, *where = NULL, *escaped_source = NULL; const char *options; int r; @@ -810,11 +827,61 @@ static int mount_overlay(const char *dest, CustomMount *m) { return mount_verbose(LOG_ERR, "overlay", where, "overlay", m->read_only ? MS_RDONLY : 0, options); } +static int mount_inaccessible(const char *dest, CustomMount *m) { + _cleanup_free_ char *where = NULL; + const char *source; + struct stat st; + int r; + + assert(dest); + assert(m); + + r = chase_symlinks_and_stat(m->destination, dest, CHASE_PREFIX_ROOT, &where, &st); + if (r < 0) { + log_full_errno(m->graceful ? LOG_DEBUG : LOG_ERR, r, "Failed to resolve %s/%s: %m", dest, m->destination); + return m->graceful ? 0 : r; + } + + assert_se(source = mode_to_inaccessible_node(st.st_mode)); + + r = mount_verbose(m->graceful ? LOG_DEBUG : LOG_ERR, source, where, NULL, MS_BIND, NULL); + if (r < 0) + return m->graceful ? 0 : r; + + r = mount_verbose(m->graceful ? LOG_DEBUG : LOG_ERR, NULL, where, NULL, MS_BIND|MS_RDONLY|MS_REMOUNT, NULL); + if (r < 0) { + umount_verbose(where); + return m->graceful ? 0 : r; + } + + return 0; +} + +static int mount_arbitrary(const char *dest, CustomMount *m) { + _cleanup_free_ char *where = NULL; + int r; + + assert(dest); + assert(m); + + r = chase_symlinks(m->destination, dest, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &where); + if (r < 0) + return log_error_errno(r, "Failed to resolve %s/%s: %m", dest, m->destination); + if (r == 0) { /* Doesn't exist yet? */ + r = mkdir_p_label(where, 0755); + if (r < 0) + return log_error_errno(r, "Creating mount point for mount %s failed: %m", where); + } + + return mount_verbose(LOG_ERR, m->source, where, m->type_argument, 0, m->options); +} + 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) { + const char *selinux_apifs_context, + bool in_userns) { size_t i; int r; @@ -824,6 +891,9 @@ int mount_custom( for (i = 0; i < n; i++) { CustomMount *m = mounts + i; + if (m->in_userns != in_userns) + continue; + switch (m->type) { case CUSTOM_MOUNT_BIND: @@ -838,6 +908,14 @@ int mount_custom( r = mount_overlay(dest, m); break; + case CUSTOM_MOUNT_INACCESSIBLE: + r = mount_inaccessible(dest, m); + break; + + case CUSTOM_MOUNT_ARBITRARY: + r = mount_arbitrary(dest, m); + break; + default: assert_not_reached("Unknown custom mount type"); } @@ -849,9 +927,8 @@ int mount_custom( return 0; } -int setup_volatile_state( +static int setup_volatile_state( const char *directory, - VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context) { @@ -861,13 +938,9 @@ int setup_volatile_state( assert(directory); - if (mode != VOLATILE_STATE) - return 0; + /* --volatile=state means we simply overmount /var with a tmpfs, and the rest read-only. */ - /* --volatile=state means we simply overmount /var - with a tmpfs, and the rest read-only. */ - - r = bind_remount_recursive(directory, true, NULL); + r = bind_remount_recursive(directory, MS_RDONLY, MS_RDONLY, NULL); if (r < 0) return log_error_errno(r, "Failed to remount %s read-only: %m", directory); @@ -886,9 +959,8 @@ int setup_volatile_state( return mount_verbose(LOG_ERR, "tmpfs", p, "tmpfs", MS_STRICTATIME, options); } -int setup_volatile( +static int setup_volatile_yes( const char *directory, - VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context) { @@ -900,11 +972,8 @@ int setup_volatile( assert(directory); - if (mode != VOLATILE_YES) - return 0; - - /* --volatile=yes means we mount a tmpfs to the root dir, and - the original /usr to use inside it, and that read-only. */ + /* --volatile=yes means we mount a tmpfs to the root dir, and the original /usr to use inside it, and that + read-only. */ if (!mkdtemp(template)) return log_error_errno(errno, "Failed to create temporary directory: %m"); @@ -912,7 +981,7 @@ int setup_volatile( options = "mode=755"; r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf); if (r < 0) - return log_oom(); + goto fail; if (r > 0) options = buf; @@ -937,7 +1006,7 @@ int setup_volatile( bind_mounted = true; - r = bind_remount_recursive(t, true, NULL); + r = bind_remount_recursive(t, MS_RDONLY, MS_RDONLY, NULL); if (r < 0) { log_error_errno(r, "Failed to remount %s read-only: %m", t); goto fail; @@ -961,6 +1030,93 @@ fail: return r; } +static int setup_volatile_overlay( + const char *directory, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + _cleanup_free_ char *buf = NULL, *escaped_directory = NULL, *escaped_upper = NULL, *escaped_work = NULL; + char template[] = "/tmp/nspawn-volatile-XXXXXX"; + const char *upper, *work, *options; + bool tmpfs_mounted = false; + int r; + + assert(directory); + + /* --volatile=overlay means we mount an overlayfs to the root dir. */ + + if (!mkdtemp(template)) + return log_error_errno(errno, "Failed to create temporary directory: %m"); + + options = "mode=755"; + r = tmpfs_patch_options(options, uid_shift == 0 ? UID_INVALID : uid_shift, selinux_apifs_context, &buf); + if (r < 0) + goto finish; + if (r > 0) + options = buf; + + r = mount_verbose(LOG_ERR, "tmpfs", template, "tmpfs", MS_STRICTATIME, options); + if (r < 0) + goto finish; + + tmpfs_mounted = true; + + upper = strjoina(template, "/upper"); + work = strjoina(template, "/work"); + + if (mkdir(upper, 0755) < 0) { + r = log_error_errno(errno, "Failed to create %s: %m", upper); + goto finish; + } + if (mkdir(work, 0755) < 0) { + r = log_error_errno(errno, "Failed to create %s: %m", work); + goto finish; + } + + /* And now, let's overmount the root dir with an overlayfs that uses the root dir as lower dir. It's kinda nice + * that the kernel allows us to do that without going through some mount point rearrangements. */ + + escaped_directory = shell_escape(directory, ",:"); + escaped_upper = shell_escape(upper, ",:"); + escaped_work = shell_escape(work, ",:"); + if (!escaped_directory || !escaped_upper || !escaped_work) { + r = -ENOMEM; + goto finish; + } + + options = strjoina("lowerdir=", escaped_directory, ",upperdir=", escaped_upper, ",workdir=", escaped_work); + r = mount_verbose(LOG_ERR, "overlay", directory, "overlay", 0, options); + +finish: + if (tmpfs_mounted) + (void) umount_verbose(template); + + (void) rmdir(template); + return r; +} + +int setup_volatile_mode( + const char *directory, + VolatileMode mode, + bool userns, uid_t uid_shift, uid_t uid_range, + const char *selinux_apifs_context) { + + switch (mode) { + + case VOLATILE_YES: + return setup_volatile_yes(directory, userns, uid_shift, uid_range, selinux_apifs_context); + + case VOLATILE_STATE: + return setup_volatile_state(directory, userns, uid_shift, uid_range, selinux_apifs_context); + + case VOLATILE_OVERLAY: + return setup_volatile_overlay(directory, userns, uid_shift, uid_range, selinux_apifs_context); + + default: + return 0; + } +} + /* Expects *pivot_root_new and *pivot_root_old to be initialised to allocated memory or NULL. */ int pivot_root_parse(char **pivot_root_new, char **pivot_root_old, const char *s) { _cleanup_free_ char *root_new = NULL, *root_old = NULL; diff --git a/src/nspawn/nspawn-mount.h b/src/nspawn/nspawn-mount.h index 8051a7d9d..ff6990c73 100644 --- a/src/nspawn/nspawn-mount.h +++ b/src/nspawn/nspawn-mount.h @@ -13,14 +13,15 @@ typedef enum MountSettingsMask { MOUNT_APPLY_APIVFS_RO = 1 << 3, /* if set, /proc/sys, and /sys will be mounted read-only, otherwise read-write. */ 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 */ + MOUNT_APPLY_TMPFS_TMP = 1 << 5, /* if set, /tmp will be mounted as tmpfs */ } MountSettingsMask; typedef enum CustomMountType { CUSTOM_MOUNT_BIND, CUSTOM_MOUNT_TMPFS, CUSTOM_MOUNT_OVERLAY, + CUSTOM_MOUNT_INACCESSIBLE, + CUSTOM_MOUNT_ARBITRARY, _CUSTOM_MOUNT_TYPE_MAX, _CUSTOM_MOUNT_TYPE_INVALID = -1 } CustomMountType; @@ -34,6 +35,9 @@ typedef struct CustomMount { char *work_dir; char **lower; char *rm_rf_tmpdir; + char *type_argument; /* only for CUSTOM_MOUNT_ARBITRARY */ + bool graceful; + bool in_userns; } CustomMount; CustomMount* custom_mount_add(CustomMount **l, size_t *n, CustomMountType t); @@ -43,14 +47,14 @@ int custom_mount_prepare_all(const char *dest, CustomMount *l, size_t n); 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 inaccessible_mount_parse(CustomMount **l, size_t *n, const char *s); 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_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 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, bool in_userns); -int setup_volatile(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); -int setup_volatile_state(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); +int setup_volatile_mode(const char *directory, VolatileMode mode, bool userns, uid_t uid_shift, uid_t uid_range, const char *selinux_apifs_context); 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); diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c new file mode 100644 index 000000000..db8e4d3b7 --- /dev/null +++ b/src/nspawn/nspawn-oci.c @@ -0,0 +1,2269 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#if HAVE_SECCOMP +#include +#endif + +#include "bus-util.h" +#include "cap-list.h" +#include "cpu-set-util.h" +#include "env-util.h" +#include "format-util.h" +#include "fs-util.h" +#include "hostname-util.h" +#include "json.h" +#include "missing_sched.h" +#include "nspawn-oci.h" +#include "path-util.h" +#include "rlimit-util.h" +#if HAVE_SECCOMP +#include "seccomp-util.h" +#endif +#include "stat-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "strv.h" +#include "user-util.h" + +/* TODO: + * OCI runtime tool implementation + * hooks + * + * Spec issues: + * + * How is RLIM_INFINITY supposed to be encoded? + * configured effective caps is bullshit, as execv() corrupts it anyway + * pipes bind mounted is *very* different from pipes newly created, comments regarding bind mount or not are bogus + * annotation values structured? or string? + * configurable file system namespace path, but then also root path? wtf? + * apply sysctl inside of the container? or outside? + * how is unlimited pids tasks limit to be encoded? + * what are the defaults for caps if not specified? + * what are the default uid/gid mappings if one is missing but the other set, or when user ns is on but no namespace configured + * the source field of "mounts" is really weird, as it cannot realistically be relative to the bundle, since we never know if that's what the fs wants + * spec contradicts itself on the mount "type" field, as the example uses "bind" as type, but it's not listed in /proc/filesystem, and is something made up by /bin/mount + * if type of mount is left out, what shall be assumed? "bind"? + * readonly mounts is entirely redundant? + * should escaping be applied when joining mount options with ","? + * devices cgroup support is bogus, "allow" and "deny" on the kernel level is about adding/removing entries, not about access + * spec needs to say that "rwm" devices cgroup combination can't be the empty string + * cgrouspv1 crap: kernel, kernelTCP, swapiness, disableOOMKiller, swap, devices, leafWeight + * general: it shouldn't leak lower level abstractions this obviously + * unmanagable cgroups stuff: realtimeRuntime/realtimePeriod + * needs to say what happense when some option is not specified, i.e. which defautls apply + * no architecture? no personality? + * seccomp example and logic is simply broken: there's no constant "SCMP_ACT_ERRNO". + * spec should say what to do with unknown props + * /bin/mount regarding NFS and FUSE required? + * what does terminal=false mean? + * sysctl inside or outside? whitelisting? + * + * Unsupported: + * + * apparmorProfile + * selinuxLabel + mountLabel + * hugepageLimits + * network + * rdma + * intelRdt + * swappiness, disableOOMKiller, kernel, kernelTCP, leafWeight (because it's dead, cgroupsv2 can't do it and hence systemd neither) + * + * Non-slice cgroup paths + * Propagation that is not slave + shared + * more than one uid/gid mapping, mappings with a container base != 0, or non-matching uid/gid mappings + * device cgroups access = false items that are not catchall + * device cgroups matches where minor is specified, but major isn't. similar where major is specified but char/block is not. also, any match that only has a type set that has less than "rwm" set. also, any entry that has none of rwm set. + * + */ + +static int oci_unexpected(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Unexpected OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v))); +} + +static int oci_unsupported(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Unsupported OCI element '%s' of type '%s'.", name, json_variant_type_to_string(json_variant_type(v))); +} + +static int oci_terminal(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + + /* If not specified, or set to true, we'll default to either an interactive or a read-only + * console. If specified as false, we'll forcibly move to "pipe" mode though. */ + s->console_mode = json_variant_boolean(v) ? _CONSOLE_MODE_INVALID : CONSOLE_PIPE; + return 0; +} + +static int oci_console_dimension(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + unsigned *u = userdata; + uintmax_t k; + + assert(u); + + k = json_variant_unsigned(variant); + if (k == 0) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), + "Console size field '%s' is too small.", strna(name)); + if (k > USHRT_MAX) /* TIOCSWINSZ's struct winsize uses "unsigned short" for width and height */ + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), + "Console size field '%s' is too large.", strna(name)); + + *u = (unsigned) k; + return 0; +} + +static int oci_console_size(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "height", JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_height), JSON_MANDATORY }, + { "width", JSON_VARIANT_UNSIGNED, oci_console_dimension, offsetof(Settings, console_width), JSON_MANDATORY }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_absolute_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + char **p = userdata; + const char *n; + + assert(p); + + n = json_variant_string(v); + + if (!path_is_absolute(n)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Path in JSON field '%s' is not absolute: %s", strna(name), n); + + return free_and_strdup_warn(p, n); +} + +static int oci_env(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + char ***l = userdata; + JsonVariant *e; + int r; + + assert(l); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + const char *n; + + if (!json_variant_is_string(e)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Environment array contains non-string."); + + assert_se(n = json_variant_string(e)); + + if (!env_assignment_is_valid(n)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Environment assignment not valid: %s", n); + + r = strv_extend(l, n); + if (r < 0) + return log_oom(); + } + + return 0; +} + +static int oci_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + _cleanup_strv_free_ char **l = NULL; + char ***value = userdata; + JsonVariant *e; + int r; + + assert(value); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + const char *n; + + if (!json_variant_is_string(e)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Argument is not a string."); + + assert_se(n = json_variant_string(e)); + + r = strv_extend(&l, n); + if (r < 0) + return log_oom(); + } + + if (strv_isempty(l)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Argument list empty, refusing."); + + if (isempty(l[0])) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Executable name is empty, refusing."); + + return strv_free_and_replace(*value, l); +} + +static int oci_rlimit_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + const char *z; + int t, *type = userdata; + + assert_se(type); + + z = startswith(json_variant_string(v), "RLIMIT_"); + if (!z) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "rlimit entry's name does not begin with 'RLIMIT_', refusing: %s", + json_variant_string(v)); + + t = rlimit_from_string(z); + if (t < 0) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "rlimit name unknown: %s", json_variant_string(v)); + + *type = t; + return 0; +} + +static int oci_rlimit_value(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + rlim_t z, *value = userdata; + + assert(value); + + if (json_variant_is_negative(v)) + z = RLIM_INFINITY; + else { + if (!json_variant_is_unsigned(v)) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "rlimits limit not unsigned, refusing."); + + z = (rlim_t) json_variant_unsigned(v); + + if ((uintmax_t) z != json_variant_unsigned(v)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "rlimits limit out of range, refusing."); + } + + *value = z; + return 0; +} + +static int oci_rlimits(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + + struct rlimit_data { + int type; + rlim_t soft; + rlim_t hard; + } data = { + .type = -1, + .soft = RLIM_INFINITY, + .hard = RLIM_INFINITY, + }; + + static const JsonDispatch table[] = { + { "soft", JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, soft), JSON_MANDATORY }, + { "hard", JSON_VARIANT_NUMBER, oci_rlimit_value, offsetof(struct rlimit_data, hard), JSON_MANDATORY }, + { "type", JSON_VARIANT_STRING, oci_rlimit_type, offsetof(struct rlimit_data, type), JSON_MANDATORY }, + {} + }; + + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + assert(data.type >= 0); + assert(data.type < _RLIMIT_MAX); + + if (s->rlimit[data.type]) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "rlimits array contains duplicate entry, refusing."); + + s->rlimit[data.type] = new(struct rlimit, 1); + if (!s->rlimit[data.type]) + return log_oom(); + + *s->rlimit[data.type] = (struct rlimit) { + .rlim_cur = data.soft, + .rlim_max = data.hard, + }; + + } + return 0; +} + +static int oci_capability_array(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uint64_t *mask = userdata, m = 0; + JsonVariant *e; + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + const char *n; + int cap; + + if (!json_variant_is_string(e)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Entry in capabilities array is not a string."); + + assert_se(n = json_variant_string(e)); + + cap = capability_from_name(n); + if (cap < 0) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Unknown capability: %s", n); + + m |= UINT64_C(1) << cap; + } + + if (*mask == (uint64_t) -1) + *mask = m; + else + *mask |= m; + + return 0; +} + +static int oci_capabilities(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "effective", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, effective) }, + { "bounding", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, bounding) }, + { "inheritable", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, inheritable) }, + { "permitted", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, permitted) }, + { "ambient", JSON_VARIANT_ARRAY, oci_capability_array, offsetof(CapabilityQuintet, ambient) }, + {} + }; + + Settings *s = userdata; + int r; + + assert(s); + + r = json_dispatch(v, table, oci_unexpected, flags, &s->full_capabilities); + if (r < 0) + return r; + + if (s->full_capabilities.bounding != (uint64_t) -1) { + s->capability = s->full_capabilities.bounding; + s->drop_capability = ~s->full_capabilities.bounding; + } + + return 0; +} + +static int oci_oom_score_adj(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + intmax_t k; + + assert(s); + + k = json_variant_integer(v); + if (k < OOM_SCORE_ADJ_MIN || k > OOM_SCORE_ADJ_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "oomScoreAdj value out of range: %ji", k); + + s->oom_score_adjust = (int) k; + s->oom_score_adjust_set = true; + + return 0; +} + +static int oci_uid_gid(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uid_t *uid = userdata, u; + uintmax_t k; + + assert(uid); + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + k = json_variant_unsigned(v); + u = (uid_t) k; + if ((uintmax_t) u != k) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "UID/GID out of range: %ji", k); + + if (!uid_is_valid(u)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "UID/GID is not valid: " UID_FMT, u); + + *uid = u; + return 0; +} + +static int oci_supplementary_gids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + gid_t gid, *a; + + if (!json_variant_is_unsigned(e)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Supplementary GID entry is not a UID."); + + r = oci_uid_gid(name, e, flags, &gid); + if (r < 0) + return r; + + a = reallocarray(s->supplementary_gids, s->n_supplementary_gids + 1, sizeof(gid_t)); + if (!a) + return log_oom(); + + s->supplementary_gids = a; + s->supplementary_gids[s->n_supplementary_gids++] = gid; + } + + return 0; +} + +static int oci_user(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + static const JsonDispatch table[] = { + { "uid", JSON_VARIANT_UNSIGNED, oci_uid_gid, offsetof(Settings, uid), JSON_MANDATORY }, + { "gid", JSON_VARIANT_UNSIGNED, oci_uid_gid, offsetof(Settings, gid), JSON_MANDATORY }, + { "additionalGids", JSON_VARIANT_ARRAY, oci_supplementary_gids, 0, 0 }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_process(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "terminal", JSON_VARIANT_BOOLEAN, oci_terminal, 0, 0 }, + { "consoleSize", JSON_VARIANT_OBJECT, oci_console_size, 0, 0 }, + { "cwd", JSON_VARIANT_STRING, oci_absolute_path, offsetof(Settings, working_directory), 0 }, + { "env", JSON_VARIANT_ARRAY, oci_env, offsetof(Settings, environment), 0 }, + { "args", JSON_VARIANT_ARRAY, oci_args, offsetof(Settings, parameters), 0 }, + { "rlimits", JSON_VARIANT_ARRAY, oci_rlimits, 0, 0 }, + { "apparmorProfile", JSON_VARIANT_STRING, oci_unsupported, 0, JSON_PERMISSIVE }, + { "capabilities", JSON_VARIANT_OBJECT, oci_capabilities, 0, 0 }, + { "noNewPrivileges", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(Settings, no_new_privileges), 0 }, + { "oomScoreAdj", JSON_VARIANT_INTEGER, oci_oom_score_adj, 0, 0 }, + { "selinuxLabel", JSON_VARIANT_STRING, oci_unsupported, 0, JSON_PERMISSIVE }, + { "user", JSON_VARIANT_OBJECT, oci_user, 0, 0 }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_root(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "path", JSON_VARIANT_STRING, json_dispatch_string, offsetof(Settings, root) }, + { "readonly", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(Settings, read_only) }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_hostname(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + const char *n; + + assert(s); + + assert_se(n = json_variant_string(v)); + + if (!hostname_is_valid(n, false)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Hostname string is not a valid hostname: %s", n); + + return free_and_strdup_warn(&s->hostname, n); +} + +static bool oci_exclude_mount(const char *path) { + + /* Returns "true" for all mounts we insist to mount on our own, and hence ignore the OCI data. */ + + if (PATH_IN_SET(path, + "/dev", + "/dev/mqueue", + "/dev/pts", + "/dev/shm", + "/proc", + "/proc/acpi", + "/proc/apm", + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/kallsyms", + "/proc/kcore", + "/proc/keys", + "/proc/scsi", + "/proc/sys", + "/proc/sys/net", + "/proc/sysrq-trigger", + "/proc/timer_list", + "/run", + "/sys", + "/sys", + "/sys/fs/selinux", + "/tmp")) + return true; + + /* Similar, skip the whole /sys/fs/cgroups subtree */ + if (path_startswith(path, "/sys/fs/cgroup")) + return true; + + return false; +} + +typedef struct oci_mount_data { + char *destination; + char *source; + char *type; + char **options; +} oci_mount_data; + +static void cleanup_oci_mount_data(oci_mount_data *data) { + free(data->destination); + free(data->source); + strv_free(data->options); + free(data->type); +} + +static int oci_mounts(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + static const JsonDispatch table[] = { + { "destination", JSON_VARIANT_STRING, oci_absolute_path, offsetof(oci_mount_data, destination), JSON_MANDATORY }, + { "source", JSON_VARIANT_STRING, json_dispatch_string, offsetof(oci_mount_data, source), 0 }, + { "options", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(oci_mount_data, options), 0, }, + { "type", JSON_VARIANT_STRING, json_dispatch_string, offsetof(oci_mount_data, type), 0 }, + {} + }; + + _cleanup_free_ char *joined_options = NULL; + CustomMount *m; + _cleanup_(cleanup_oci_mount_data) oci_mount_data data = {}; + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + if (!path_is_absolute(data.destination)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Mount destination not an absolute path: %s", data.destination); + + if (oci_exclude_mount(data.destination)) + continue; + + if (data.options) { + joined_options = strv_join(data.options, ","); + if (!joined_options) + return log_oom(); + } + + if (!data.type || streq(data.type, "bind")) { + if (data.source && !path_is_absolute(data.source)) { + char *joined; + + joined = path_join(s->bundle, data.source); + if (!joined) + return log_oom(); + + free_and_replace(data.source, joined); + } + + data.type = mfree(data.type); + + m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND); + } else + m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_ARBITRARY); + if (!m) + return log_oom(); + + m->destination = TAKE_PTR(data.destination); + m->source = TAKE_PTR(data.source); + m->options = TAKE_PTR(joined_options); + m->type_argument = TAKE_PTR(data.type); + } + + return 0; +} + +static int oci_namespace_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + unsigned long *nsflags = userdata; + const char *n; + + assert(nsflags); + assert_se(n = json_variant_string(v)); + + /* We don't use namespace_flags_from_string() here, as the OCI spec uses slightly different names than the + * kernel here. */ + if (streq(n, "pid")) + *nsflags = CLONE_NEWPID; + else if (streq(n, "network")) + *nsflags = CLONE_NEWNET; + else if (streq(n, "mount")) + *nsflags = CLONE_NEWNS; + else if (streq(n, "ipc")) + *nsflags = CLONE_NEWIPC; + else if (streq(n, "uts")) + *nsflags = CLONE_NEWUTS; + else if (streq(n, "user")) + *nsflags = CLONE_NEWUSER; + else if (streq(n, "cgroup")) + *nsflags = CLONE_NEWCGROUP; + else + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Unknown cgroup type, refusing: %s", n); + + return 0; +} + +static int oci_namespaces(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + unsigned long n = 0; + JsonVariant *e; + int r; + + assert_se(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + + struct namespace_data { + unsigned long type; + char *path; + } data = {}; + + static const JsonDispatch table[] = { + { "type", JSON_VARIANT_STRING, oci_namespace_type, offsetof(struct namespace_data, type), JSON_MANDATORY }, + { "path", JSON_VARIANT_STRING, oci_absolute_path, offsetof(struct namespace_data, path), 0 }, + {} + }; + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) { + free(data.path); + return r; + } + + if (data.path) { + if (data.type != CLONE_NEWNET) { + free(data.path); + return json_log(e, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Specifying namespace path for non-network namespace is not supported."); + } + + if (s->network_namespace_path) { + free(data.path); + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Network namespace path specified more than once, refusing."); + } + + free(s->network_namespace_path); + s->network_namespace_path = data.path; + } + + if (FLAGS_SET(n, data.type)) { + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Duplicate namespace specification, refusing."); + } + + n |= data.type; + } + + if (!FLAGS_SET(n, CLONE_NEWNS)) + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Containers without file system namespace aren't supported."); + + s->private_network = FLAGS_SET(n, CLONE_NEWNET); + s->userns_mode = FLAGS_SET(n, CLONE_NEWUSER) ? USER_NAMESPACE_FIXED : USER_NAMESPACE_NO; + s->use_cgns = FLAGS_SET(n, CLONE_NEWCGROUP); + + s->clone_ns_flags = n & (CLONE_NEWIPC|CLONE_NEWPID|CLONE_NEWUTS); + + return 0; +} + +static int oci_uid_gid_range(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uid_t *uid = userdata, u; + uintmax_t k; + + assert(uid); + assert_cc(sizeof(uid_t) == sizeof(gid_t)); + + /* This is very much like oci_uid_gid(), except the checks are a bit different, as this is a UID range rather + * than a specific UID, and hence (uid_t) -1 has no special significance. OTOH a range of zero makes no + * sense. */ + + k = json_variant_unsigned(v); + u = (uid_t) k; + if ((uintmax_t) u != k) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "UID/GID out of range: %ji", k); + if (u == 0) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "UID/GID range can't be zero."); + + *uid = u; + return 0; +} + +static int oci_uid_gid_mappings(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + struct mapping_data { + uid_t host_id; + uid_t container_id; + uid_t range; + } data = { + .host_id = UID_INVALID, + .container_id = UID_INVALID, + .range = 0, + }; + + static const JsonDispatch table[] = { + { "containerID", JSON_VARIANT_UNSIGNED, oci_uid_gid, offsetof(struct mapping_data, container_id), JSON_MANDATORY }, + { "hostID", JSON_VARIANT_UNSIGNED, oci_uid_gid, offsetof(struct mapping_data, host_id), JSON_MANDATORY }, + { "size", JSON_VARIANT_UNSIGNED, oci_uid_gid_range, offsetof(struct mapping_data, range), JSON_MANDATORY }, + {} + }; + + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + if (json_variant_elements(v) == 0) + return 0; + + if (json_variant_elements(v) > 1) + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "UID/GID mappings with more than one entry are not supported."); + + assert_se(e = json_variant_by_index(v, 0)); + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + if (data.host_id + data.range < data.host_id || + data.container_id + data.range < data.container_id) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "UID/GID range goes beyond UID/GID validity range, refusing."); + + if (data.container_id != 0) + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "UID/GID mappings with a non-zero container base are not supported."); + + if (data.range < 0x10000) + json_log(v, flags|JSON_WARNING, 0, + "UID/GID mapping with less than 65536 UID/GIDS set up, you are looking for trouble."); + + if (s->uid_range != UID_INVALID && + (s->uid_shift != data.host_id || s->uid_range != data.range)) + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Non-matching UID and GID mappings are not supported."); + + s->uid_shift = data.host_id; + s->uid_range = data.range; + + return 0; +} + +static int oci_device_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + mode_t *mode = userdata; + const char *t; + + assert(mode); + assert_se(t = json_variant_string(v)); + + if (STR_IN_SET(t, "c", "u")) + *mode = (*mode & ~S_IFMT) | S_IFCHR; + else if (streq(t, "b")) + *mode = (*mode & ~S_IFMT) | S_IFBLK; + else if (streq(t, "p")) + *mode = (*mode & ~S_IFMT) | S_IFIFO; + else + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Unknown device type: %s", t); + + return 0; +} + +static int oci_device_major(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + unsigned *u = userdata; + uintmax_t k; + + assert_se(u); + + k = json_variant_unsigned(v); + if (!DEVICE_MAJOR_VALID(k)) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "Device major %ji out of range.", k); + + *u = (unsigned) k; + return 0; +} + +static int oci_device_minor(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + unsigned *u = userdata; + uintmax_t k; + + assert_se(u); + + k = json_variant_unsigned(v); + if (!DEVICE_MINOR_VALID(k)) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "Device minor %ji out of range.", k); + + *u = (unsigned) k; + return 0; +} + +static int oci_device_file_mode(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + mode_t *mode = userdata, m; + uintmax_t k; + + assert(mode); + + k = json_variant_unsigned(v); + m = (mode_t) k; + + if ((m & ~07777) != 0 || (uintmax_t) m != k) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "fileMode out of range, refusing."); + + *mode = m; + return 0; +} + +static int oci_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + + static const JsonDispatch table[] = { + { "type", JSON_VARIANT_STRING, oci_device_type, offsetof(DeviceNode, mode), JSON_MANDATORY }, + { "path", JSON_VARIANT_STRING, oci_absolute_path, offsetof(DeviceNode, path), JSON_MANDATORY }, + { "major", JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(DeviceNode, major), 0 }, + { "minor", JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(DeviceNode, minor), 0 }, + { "fileMode", JSON_VARIANT_UNSIGNED, oci_device_file_mode, offsetof(DeviceNode, mode), 0 }, + { "uid", JSON_VARIANT_UNSIGNED, oci_uid_gid, offsetof(DeviceNode, uid), 0 }, + { "gid", JSON_VARIANT_UNSIGNED, oci_uid_gid, offsetof(DeviceNode, gid), 0 }, + {} + }; + + DeviceNode *node, *nodes; + + nodes = reallocarray(s->extra_nodes, s->n_extra_nodes + 1, sizeof(DeviceNode)); + if (!nodes) + return log_oom(); + + s->extra_nodes = nodes; + + node = nodes + s->n_extra_nodes; + *node = (DeviceNode) { + .uid = UID_INVALID, + .gid = GID_INVALID, + .major = (unsigned) -1, + .minor = (unsigned) -1, + .mode = 0644, + }; + + r = json_dispatch(e, table, oci_unexpected, flags, node); + if (r < 0) + goto fail_element; + + if (S_ISCHR(node->mode) || S_ISBLK(node->mode)) { + _cleanup_free_ char *path = NULL; + + if (node->major == (unsigned) -1 || node->minor == (unsigned) -1) { + r = json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Major/minor required when device node is device node"); + goto fail_element; + } + + /* Suppress a couple of implicit device nodes */ + r = device_path_make_canonical(node->mode, makedev(node->major, node->minor), &path); + if (r < 0) + json_log(e, flags|JSON_DEBUG, 0, "Failed to resolve device node %u:%u, ignoring: %m", node->major, node->minor); + else { + if (PATH_IN_SET(path, + "/dev/null", + "/dev/zero", + "/dev/full", + "/dev/random", + "/dev/urandom", + "/dev/tty", + "/dev/net/tun", + "/dev/ptmx", + "/dev/pts/ptmx", + "/dev/console")) { + + json_log(e, flags|JSON_DEBUG, 0, "Ignoring devices item for device '%s', as it is implicitly created anyway.", path); + free(node->path); + continue; + } + } + } + + s->n_extra_nodes++; + continue; + + fail_element: + free(node->path); + return r; + } + + return 0; +} + +static int oci_cgroups_path(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + _cleanup_free_ char *slice = NULL, *backwards = NULL; + Settings *s = userdata; + const char *p; + int r; + + assert(s); + + assert_se(p = json_variant_string(v)); + + r = cg_path_get_slice(p, &slice); + if (r < 0) + return json_log(v, flags, r, "Couldn't derive slice unit name from path '%s': %m", p); + + r = cg_slice_to_path(slice, &backwards); + if (r < 0) + return json_log(v, flags, r, "Couldn't convert slice unit name '%s' back to path: %m", slice); + + if (!path_equal(backwards, p)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Control group path '%s' does not refer to slice unit, refusing.", p); + + free_and_replace(s->slice, slice); + return 0; +} + +static int oci_cgroup_device_type(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + mode_t *mode = userdata; + const char *n; + + assert_se(n = json_variant_string(v)); + + if (streq(n, "c")) + *mode = S_IFCHR; + else if (streq(n, "b")) + *mode = S_IFBLK; + else + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Control group device type unknown: %s", n); + + return 0; +} + +struct device_data { + bool allow; + bool r; + bool w; + bool m; + mode_t type; + unsigned major; + unsigned minor; +}; + +static int oci_cgroup_device_access(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + struct device_data *d = userdata; + bool r = false, w = false, m = false; + const char *s; + size_t i; + + assert_se(s = json_variant_string(v)); + + for (i = 0; s[i]; i++) + if (s[i] == 'r') + r = true; + else if (s[i] == 'w') + w = true; + else if (s[i] == 'm') + m = true; + else + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Unknown device access character '%c'.", s[i]); + + d->r = r; + d->w = w; + d->m = m; + + return 0; +} + +static int oci_cgroup_devices(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + _cleanup_free_ struct device_data *list = NULL; + Settings *s = userdata; + size_t n_list = 0, i; + bool noop = false; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + + struct device_data data = { + .major = (unsigned) -1, + .minor = (unsigned) -1, + }, *a; + + static const JsonDispatch table[] = { + { "allow", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(struct device_data, allow), JSON_MANDATORY }, + { "type", JSON_VARIANT_STRING, oci_cgroup_device_type, offsetof(struct device_data, type), 0 }, + { "major", JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(struct device_data, major), 0 }, + { "minor", JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(struct device_data, minor), 0 }, + { "access", JSON_VARIANT_STRING, oci_cgroup_device_access, 0, 0 }, + {} + }; + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + if (!data.allow) { + /* The fact that OCI allows 'deny' entries makes really no sense, as 'allow' vs. 'deny' for the + * devices cgroup controller is really not about whitelisting and blacklisting but about adding + * and removing entries from the whitelist. Since we always start out with an empty whitelist + * we hence ignore the whole thing, as removing entries which don't exist make no sense. We'll + * log about this, since this is really borked in the spec, with one exception: the entry + * that's supposed to drop the kernel's default we ignore silently */ + + if (!data.r || !data.w || !data.m || data.type != 0 || data.major != (unsigned) -1 || data.minor != (unsigned) -1) + json_log(v, flags|JSON_WARNING, 0, "Devices cgroup whitelist with arbitrary 'allow' entries not supported, ignoring."); + + /* We ignore the 'deny' entry as for us that's implied */ + continue; + } + + if (!data.r && !data.w && !data.m) { + json_log(v, flags|LOG_WARNING, 0, "Device cgroup whitelist entry with no effect found, ignoring."); + continue; + } + + if (data.minor != (unsigned) -1 && data.major == (unsigned) -1) + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Device cgroup whitelist entries with minors but no majors not supported."); + + if (data.major != (unsigned) -1 && data.type == 0) + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Device cgroup whitelist entries with majors but no device node type not supported."); + + if (data.type == 0) { + if (data.r && data.w && data.m) /* a catchall whitelist entry means we are looking at a noop */ + noop = true; + else + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), + "Device cgroup whitelist entries with no type not supported."); + } + + a = reallocarray(list, n_list + 1, sizeof(struct device_data)); + if (!a) + return log_oom(); + + list = a; + list[n_list++] = data; + } + + if (noop) + return 0; + + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_open_container(s->properties, 'r', "sv"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_append(s->properties, "s", "DeviceAllow"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(s->properties, 'v', "a(ss)"); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_open_container(s->properties, 'a', "(ss)"); + if (r < 0) + return bus_log_create_error(r); + + for (i = 0; i < n_list; i++) { + _cleanup_free_ char *pattern = NULL; + char access[4]; + size_t n = 0; + + if (list[i].minor == (unsigned) -1) { + const char *t; + + if (list[i].type == S_IFBLK) + t = "block"; + else { + assert(list[i].type == S_IFCHR); + t = "char"; + } + + if (list[i].major == (unsigned) -1) { + pattern = strjoin(t, "-*"); + if (!pattern) + return log_oom(); + } else { + if (asprintf(&pattern, "%s-%u", t, list[i].major) < 0) + return log_oom(); + } + + } else { + assert(list[i].major != (unsigned) -1); /* If a minor is specified, then a major also needs to be specified */ + + r = device_path_make_major_minor(list[i].type, makedev(list[i].major, list[i].minor), &pattern); + if (r < 0) + return log_oom(); + } + + if (list[i].r) + access[n++] = 'r'; + if (list[i].w) + access[n++] = 'w'; + if (list[i].m) + access[n++] = 'm'; + access[n] = 0; + + assert(n > 0); + + r = sd_bus_message_append(s->properties, "(ss)", pattern, access); + if (r < 0) + return bus_log_create_error(r); + } + + r = sd_bus_message_close_container(s->properties); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(s->properties); + if (r < 0) + return bus_log_create_error(r); + + r = sd_bus_message_close_container(s->properties); + if (r < 0) + return bus_log_create_error(r); + + return 0; +} + +static int oci_cgroup_memory_limit(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uint64_t *m = userdata; + uintmax_t k; + + assert(m); + + if (json_variant_is_negative(v)) { + *m = UINT64_MAX; + return 0; + } + + if (!json_variant_is_unsigned(v)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Memory limit is not an unsigned integer"); + + k = json_variant_unsigned(v); + if (k >= UINT64_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "Memory limit too large: %ji", k); + + *m = (uint64_t) k; + return 0; +} + +static int oci_cgroup_memory(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + struct memory_data { + uint64_t limit; + uint64_t reservation; + uint64_t swap; + } data = { + .limit = UINT64_MAX, + .reservation = UINT64_MAX, + .swap = UINT64_MAX, + }; + + static const JsonDispatch table[] = { + { "limit", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, limit), 0 }, + { "reservation", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, reservation), 0 }, + { "swap", JSON_VARIANT_NUMBER, oci_cgroup_memory_limit, offsetof(struct memory_data, swap), 0 }, + { "kernel", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "kernelTCP", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "swapiness", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + { "disableOOMKiller", JSON_VARIANT_NUMBER, oci_unsupported, 0, JSON_PERMISSIVE }, + {} + }; + + Settings *s = userdata; + int r; + + r = json_dispatch(v, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + if (data.swap != UINT64_MAX) { + if (data.limit == UINT64_MAX) + json_log(v, flags|LOG_WARNING, 0, "swap limit without memory limit is not supported, ignoring."); + else if (data.swap < data.limit) + json_log(v, flags|LOG_WARNING, 0, "swap limit is below memory limit, ignoring."); + else { + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "MemorySwapMax", "t", data.swap - data.limit); + if (r < 0) + return bus_log_create_error(r); + } + } + + if (data.limit != UINT64_MAX) { + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "MemoryMax", "t", data.limit); + if (r < 0) + return bus_log_create_error(r); + } + + if (data.reservation != UINT64_MAX) { + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "MemoryLow", "t", data.reservation); + if (r < 0) + return bus_log_create_error(r); + } + + return 0; +} + +struct cpu_data { + uint64_t shares; + uint64_t quota; + uint64_t period; + cpu_set_t *cpuset; + unsigned ncpus; +}; + +static int oci_cgroup_cpu_shares(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uint64_t *u = userdata; + uintmax_t k; + + assert(u); + + k = json_variant_unsigned(v); + if (k < CGROUP_CPU_SHARES_MIN || k > CGROUP_CPU_SHARES_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "shares value out of range."); + + *u = (uint64_t) k; + return 0; +} + +static int oci_cgroup_cpu_quota(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uint64_t *u = userdata; + uintmax_t k; + + assert(u); + + k = json_variant_unsigned(v); + if (k <= 0 || k >= UINT64_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "period/quota value out of range."); + + *u = (uint64_t) k; + return 0; +} + +static int oci_cgroup_cpu_cpus(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + struct cpu_data *data = userdata; + cpu_set_t *set; + const char *n; + int ncpus; + + assert(data); + + assert_se(n = json_variant_string(v)); + + ncpus = parse_cpu_set(n, &set); + if (ncpus < 0) + return json_log(v, flags, ncpus, "Failed to parse CPU set specification: %s", n); + + CPU_FREE(data->cpuset); + data->cpuset = set; + data->ncpus = ncpus; + + return 0; +} + +static int oci_cgroup_cpu(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "shares", JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_shares, offsetof(struct cpu_data, shares), 0 }, + { "quota", JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota, offsetof(struct cpu_data, quota), 0 }, + { "period", JSON_VARIANT_UNSIGNED, oci_cgroup_cpu_quota, offsetof(struct cpu_data, period), 0 }, + { "realtimeRuntime", JSON_VARIANT_UNSIGNED, oci_unsupported, 0, 0 }, + { "realtimePeriod", JSON_VARIANT_UNSIGNED, oci_unsupported, 0, 0 }, + { "cpus", JSON_VARIANT_STRING, oci_cgroup_cpu_cpus, 0, 0 }, + { "mems", JSON_VARIANT_STRING, oci_unsupported, 0, 0 }, + {} + }; + + struct cpu_data data = { + .shares = UINT64_MAX, + .quota = UINT64_MAX, + .period = UINT64_MAX, + }; + + Settings *s = userdata; + int r; + + r = json_dispatch(v, table, oci_unexpected, flags, &data); + if (r < 0) { + CPU_FREE(data.cpuset); + return r; + } + + CPU_FREE(s->cpuset); + s->cpuset = data.cpuset; + s->cpuset_ncpus = data.ncpus; + + if (data.shares != UINT64_MAX) { + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "CPUShares", "t", data.shares); + if (r < 0) + return bus_log_create_error(r); + } + + if (data.quota != UINT64_MAX && data.period != UINT64_MAX) { + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "CPUQuotaPerSecUSec", "t", (uint64_t) (data.quota * USEC_PER_SEC / data.period)); + if (r < 0) + return bus_log_create_error(r); + + } else if ((data.quota != UINT64_MAX) != (data.period != UINT64_MAX)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "CPU quota and period not used together."); + + return 0; +} + +static int oci_cgroup_block_io_weight(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + uintmax_t k; + int r; + + assert(s); + + k = json_variant_unsigned(v); + if (k < CGROUP_BLKIO_WEIGHT_MIN || k > CGROUP_BLKIO_WEIGHT_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "Block I/O weight out of range."); + + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "BlockIOWeight", "t", (uint64_t) k); + if (r < 0) + return bus_log_create_error(r); + + return 0; +} + +static int oci_cgroup_block_io_weight_device(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + struct device_data { + unsigned major; + unsigned minor; + uintmax_t weight; + } data = { + .major = (unsigned) -1, + .minor = (unsigned) -1, + .weight = UINTMAX_MAX, + }; + + static const JsonDispatch table[] = { + { "major", JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(struct device_data, major), JSON_MANDATORY }, + { "minor", JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(struct device_data, minor), JSON_MANDATORY }, + { "weight", JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(struct device_data, weight), 0 }, + { "leafWeight", JSON_VARIANT_INTEGER, oci_unsupported, 0, JSON_PERMISSIVE }, + {} + }; + + _cleanup_free_ char *path = NULL; + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + if (data.weight == UINTMAX_MAX) + continue; + + if (data.weight < CGROUP_BLKIO_WEIGHT_MIN || data.weight > CGROUP_BLKIO_WEIGHT_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "Block I/O device weight out of range."); + + r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path); + if (r < 0) + return json_log(v, flags, r, "Failed to build device path: %m"); + + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "BlockIODeviceWeight", "a(st)", 1, path, (uint64_t) data.weight); + if (r < 0) + return bus_log_create_error(r); + } + + return 0; +} + +static int oci_cgroup_block_io_throttle(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + const char *pname; + JsonVariant *e; + int r; + + assert(s); + + pname = streq(name, "throttleReadBpsDevice") ? "IOReadBandwidthMax" : + streq(name, "throttleWriteBpsDevice") ? "IOWriteBandwidthMax" : + streq(name, "throttleReadIOPSDevice") ? "IOReadIOPSMax" : + "IOWriteIOPSMax"; + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + struct device_data { + unsigned major; + unsigned minor; + uintmax_t rate; + } data = { + .major = (unsigned) -1, + .minor = (unsigned) -1, + }; + + static const JsonDispatch table[] = { + { "major", JSON_VARIANT_UNSIGNED, oci_device_major, offsetof(struct device_data, major), JSON_MANDATORY }, + { "minor", JSON_VARIANT_UNSIGNED, oci_device_minor, offsetof(struct device_data, minor), JSON_MANDATORY }, + { "rate", JSON_VARIANT_UNSIGNED, json_dispatch_unsigned, offsetof(struct device_data, rate), JSON_MANDATORY }, + {} + }; + + _cleanup_free_ char *path = NULL; + + r = json_dispatch(e, table, oci_unexpected, flags, &data); + if (r < 0) + return r; + + if (data.rate >= UINT64_MAX) + return json_log(v, flags, SYNTHETIC_ERRNO(ERANGE), + "Block I/O device rate out of range."); + + r = device_path_make_major_minor(S_IFBLK, makedev(data.major, data.minor), &path); + if (r < 0) + return json_log(v, flags, r, "Failed to build device path: %m"); + + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", pname, "a(st)", 1, path, (uint64_t) data.rate); + if (r < 0) + return bus_log_create_error(r); + } + + return 0; +} + +static int oci_cgroup_block_io(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "weight", JSON_VARIANT_UNSIGNED, oci_cgroup_block_io_weight, 0, 0 }, + { "leafWeight", JSON_VARIANT_UNSIGNED, oci_unsupported, 0, JSON_PERMISSIVE }, + { "weightDevice", JSON_VARIANT_ARRAY, oci_cgroup_block_io_weight_device, 0, 0 }, + { "throttleReadBpsDevice", JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 }, + { "throttleWriteBpsDevice", JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 }, + { "throttleReadIOPSDevice", JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 }, + { "throttleWriteIOPSDevice", JSON_VARIANT_ARRAY, oci_cgroup_block_io_throttle, 0, 0 }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_cgroup_pids(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "limit", JSON_VARIANT_NUMBER, json_dispatch_variant, 0, JSON_MANDATORY }, + {} + }; + + _cleanup_(json_variant_unrefp) JsonVariant *k = NULL; + Settings *s = userdata; + uint64_t m; + int r; + + assert(s); + + r = json_dispatch(v, table, oci_unexpected, flags, &k); + if (r < 0) + return r; + + if (json_variant_is_negative(k)) + m = UINT64_MAX; + else { + if (!json_variant_is_unsigned(k)) + return json_log(k, flags, SYNTHETIC_ERRNO(EINVAL), + "pids limit not unsigned integer, refusing."); + + m = (uint64_t) json_variant_unsigned(k); + + if ((uintmax_t) m != json_variant_unsigned(k)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "pids limit out of range, refusing."); + } + + r = settings_allocate_properties(s); + if (r < 0) + return r; + + r = sd_bus_message_append(s->properties, "(sv)", "TasksMax", "t", m); + if (r < 0) + return bus_log_create_error(r); + + return 0; +} + +static int oci_resources(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "devices", JSON_VARIANT_ARRAY, oci_cgroup_devices, 0, 0 }, + { "memory", JSON_VARIANT_OBJECT, oci_cgroup_memory, 0, 0 }, + { "cpu", JSON_VARIANT_OBJECT, oci_cgroup_cpu, 0, 0 }, + { "blockIO", JSON_VARIANT_OBJECT, oci_cgroup_block_io, 0, 0 }, + { "hugepageLimits", JSON_VARIANT_ARRAY, oci_unsupported, 0, 0 }, + { "network", JSON_VARIANT_OBJECT, oci_unsupported, 0, 0 }, + { "pids", JSON_VARIANT_OBJECT, oci_cgroup_pids, 0, 0 }, + { "rdma", JSON_VARIANT_OBJECT, oci_unsupported, 0, 0 }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static bool sysctl_key_valid(const char *s) { + bool dot = true; + + /* Note that we are a bit stricter here than in systemd-sysctl, as that inherited semantics from the old sysctl + * tool, which were really weird (as it swaps / and . in both ways) */ + + if (isempty(s)) + return false; + + for (; *s; s++) { + + if (*s <= ' ' || *s >= 127) + return false; + if (*s == '/') + return false; + if (*s == '.') { + + if (dot) /* Don't allow two dots next to each other (or at the beginning) */ + return false; + + dot = true; + } else + dot = false; + } + + if (dot) /* don't allow a dot at the end */ + return false; + + return true; +} + +static int oci_sysctl(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *k, *w; + int r; + + assert(s); + + JSON_VARIANT_OBJECT_FOREACH(k, w, v) { + const char *n, *m; + + if (!json_variant_is_string(w)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "sysctl parameter is not a string, refusing."); + + assert_se(n = json_variant_string(k)); + assert_se(m = json_variant_string(w)); + + if (sysctl_key_valid(n)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "sysctl key invalid, refusing: %s", n); + + r = strv_extend_strv(&s->sysctl, STRV_MAKE(n, m), false); + if (r < 0) + return log_oom(); + } + + return 0; +} + +#if HAVE_SECCOMP +static int oci_seccomp_action_from_string(const char *name, uint32_t *ret) { + + static const struct { + const char *name; + uint32_t action; + } table[] = { + { "SCMP_ACT_ALLOW", SCMP_ACT_ALLOW }, + { "SCMP_ACT_ERRNO", SCMP_ACT_ERRNO(EPERM) }, /* the OCI spec doesn't document the error, but it appears EPERM is supposed to be used */ + { "SCMP_ACT_KILL", SCMP_ACT_KILL }, +#ifdef SCMP_ACT_LOG + { "SCMP_ACT_LOG", SCMP_ACT_LOG }, +#endif + { "SCMP_ACT_TRAP", SCMP_ACT_TRAP }, + + /* We don't support SCMP_ACT_TRACE because that requires a tracer, and that doesn't really make sense + * here */ + }; + + size_t i; + + for (i = 0; i < ELEMENTSOF(table); i++) + if (streq_ptr(name, table[i].name)) { + *ret = table[i].action; + return 0; + } + + return -EINVAL; +} + +static int oci_seccomp_arch_from_string(const char *name, uint32_t *ret) { + + static const struct { + const char *name; + uint32_t arch; + } table[] = { + { "SCMP_ARCH_AARCH64", SCMP_ARCH_AARCH64 }, + { "SCMP_ARCH_ARM", SCMP_ARCH_ARM }, + { "SCMP_ARCH_MIPS", SCMP_ARCH_MIPS }, + { "SCMP_ARCH_MIPS64", SCMP_ARCH_MIPS64 }, + { "SCMP_ARCH_MIPS64N32", SCMP_ARCH_MIPS64N32 }, + { "SCMP_ARCH_MIPSEL", SCMP_ARCH_MIPSEL }, + { "SCMP_ARCH_MIPSEL64", SCMP_ARCH_MIPSEL64 }, + { "SCMP_ARCH_MIPSEL64N32", SCMP_ARCH_MIPSEL64N32 }, + { "SCMP_ARCH_NATIVE", SCMP_ARCH_NATIVE }, +#ifdef SCMP_ARCH_PARISC + { "SCMP_ARCH_PARISC", SCMP_ARCH_PARISC }, +#endif +#ifdef SCMP_ARCH_PARISC64 + { "SCMP_ARCH_PARISC64", SCMP_ARCH_PARISC64 }, +#endif + { "SCMP_ARCH_PPC", SCMP_ARCH_PPC }, + { "SCMP_ARCH_PPC64", SCMP_ARCH_PPC64 }, + { "SCMP_ARCH_PPC64LE", SCMP_ARCH_PPC64LE }, + { "SCMP_ARCH_S390", SCMP_ARCH_S390 }, + { "SCMP_ARCH_S390X", SCMP_ARCH_S390X }, + { "SCMP_ARCH_X32", SCMP_ARCH_X32 }, + { "SCMP_ARCH_X86", SCMP_ARCH_X86 }, + { "SCMP_ARCH_X86_64", SCMP_ARCH_X86_64 }, + }; + + size_t i; + + for (i = 0; i < ELEMENTSOF(table); i++) + if (streq_ptr(table[i].name, name)) { + *ret = table[i].arch; + return 0; + } + + return -EINVAL; +} + +static int oci_seccomp_compare_from_string(const char *name, enum scmp_compare *ret) { + + static const struct { + const char *name; + enum scmp_compare op; + } table[] = { + { "SCMP_CMP_NE", SCMP_CMP_NE }, + { "SCMP_CMP_LT", SCMP_CMP_LT }, + { "SCMP_CMP_LE", SCMP_CMP_LE }, + { "SCMP_CMP_EQ", SCMP_CMP_EQ }, + { "SCMP_CMP_GE", SCMP_CMP_GE }, + { "SCMP_CMP_GT", SCMP_CMP_GT }, + { "SCMP_CMP_MASKED_EQ", SCMP_CMP_MASKED_EQ }, + }; + + size_t i; + + for (i = 0; i < ELEMENTSOF(table); i++) + if (streq_ptr(table[i].name, name)) { + *ret = table[i].op; + return 0; + } + + return -EINVAL; +} + +static int oci_seccomp_archs(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + scmp_filter_ctx *sc = userdata; + JsonVariant *e; + int r; + + assert(sc); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + uint32_t a; + + if (!json_variant_is_string(e)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), + "Architecture entry is not a string"); + + r = oci_seccomp_arch_from_string(json_variant_string(e), &a); + if (r < 0) + return json_log(e, flags, r, "Unknown architecture: %s", json_variant_string(e)); + + r = seccomp_arch_add(sc, a); + if (r == -EEXIST) + continue; + if (r < 0) + return json_log(e, flags, r, "Failed to add architecture to seccomp filter: %m"); + } + + return 0; +} + +struct syscall_rule { + char **names; + uint32_t action; + struct scmp_arg_cmp *arguments; + size_t n_arguments; +}; + +static void syscall_rule_free(struct syscall_rule *rule) { + assert(rule); + + strv_free(rule->names); + free(rule->arguments); +}; + +static int oci_seccomp_action(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + uint32_t *action = userdata; + int r; + + assert(action); + + r = oci_seccomp_action_from_string(json_variant_string(v), action); + if (r < 0) + return json_log(v, flags, r, "Unknown system call action '%s': %m", json_variant_string(v)); + + return 0; +} + +static int oci_seccomp_op(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + enum scmp_compare *op = userdata; + int r; + + assert(op); + + r = oci_seccomp_compare_from_string(json_variant_string(v), op); + if (r < 0) + return json_log(v, flags, r, "Unknown seccomp operator '%s': %m", json_variant_string(v)); + + return 0; +} + +static int oci_seccomp_args(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + struct syscall_rule *rule = userdata; + JsonVariant *e; + int r; + + assert(rule); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + static const struct JsonDispatch table[] = { + { "index", JSON_VARIANT_UNSIGNED, json_dispatch_uint32, offsetof(struct scmp_arg_cmp, arg), JSON_MANDATORY }, + { "value", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_a), JSON_MANDATORY }, + { "valueTwo", JSON_VARIANT_UNSIGNED, json_dispatch_uint64, offsetof(struct scmp_arg_cmp, datum_b), 0 }, + { "op", JSON_VARIANT_STRING, oci_seccomp_op, offsetof(struct scmp_arg_cmp, op), JSON_MANDATORY }, + {}, + }; + + struct scmp_arg_cmp *a, *p; + int expected; + + a = reallocarray(rule->arguments, rule->n_arguments + 1, sizeof(struct syscall_rule)); + if (!a) + return log_oom(); + + rule->arguments = a; + p = rule->arguments + rule->n_arguments; + + *p = (struct scmp_arg_cmp) { + .arg = 0, + .datum_a = 0, + .datum_b = 0, + .op = 0, + }; + + r = json_dispatch(e, table, oci_unexpected, flags, p); + if (r < 0) + return r; + + expected = p->op == SCMP_CMP_MASKED_EQ ? 4 : 3; + if (r != expected) + json_log(e, flags|JSON_WARNING, 0, "Wrong number of system call arguments for JSON data data, ignoring."); + + /* Note that we are a bit sloppy here and do not insist that SCMP_CMP_MASKED_EQ gets two datum values, + * and the other only one. That's because buildah for example by default calls things with + * SCMP_CMP_MASKED_EQ but only one argument. We use 0 when the value is not specified. */ + + rule->n_arguments++; + } + + return 0; +} + +static int oci_seccomp_syscalls(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + scmp_filter_ctx *sc = userdata; + JsonVariant *e; + int r; + + assert(sc); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + static const JsonDispatch table[] = { + { "names", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(struct syscall_rule, names), JSON_MANDATORY }, + { "action", JSON_VARIANT_STRING, oci_seccomp_action, offsetof(struct syscall_rule, action), JSON_MANDATORY }, + { "args", JSON_VARIANT_ARRAY, oci_seccomp_args, 0, 0 }, + }; + struct syscall_rule rule = { + .action = (uint32_t) -1, + }; + char **i; + + r = json_dispatch(e, table, oci_unexpected, flags, &rule); + if (r < 0) + goto fail_rule; + + if (strv_isempty(rule.names)) { + json_log(e, flags, 0, "System call name list is empty."); + r = -EINVAL; + goto fail_rule; + } + + STRV_FOREACH(i, rule.names) { + int nr; + + nr = seccomp_syscall_resolve_name(*i); + if (nr == __NR_SCMP_ERROR) { + log_debug("Unknown syscall %s, skipping.", *i); + continue; + } + + r = seccomp_rule_add_array(sc, rule.action, nr, rule.n_arguments, rule.arguments); + if (r < 0) + goto fail_rule; + } + + syscall_rule_free(&rule); + continue; + + fail_rule: + syscall_rule_free(&rule); + return r; + } + + return 0; +} +#endif + +static int oci_seccomp(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + +#if HAVE_SECCOMP + static const JsonDispatch table[] = { + { "defaultAction", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY }, + { "architectures", JSON_VARIANT_ARRAY, oci_seccomp_archs, 0, 0 }, + { "syscalls", JSON_VARIANT_ARRAY, oci_seccomp_syscalls, 0, 0 }, + {} + }; + + _cleanup_(seccomp_releasep) scmp_filter_ctx sc = NULL; + Settings *s = userdata; + JsonVariant *def; + uint32_t d; + int r; + + assert(s); + + def = json_variant_by_key(v, "defaultAction"); + if (!def) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction element missing."); + + if (!json_variant_is_string(def)) + return json_log(def, flags, SYNTHETIC_ERRNO(EINVAL), "defaultAction is not a string."); + + r = oci_seccomp_action_from_string(json_variant_string(def), &d); + if (r < 0) + return json_log(def, flags, r, "Unknown default action: %s", json_variant_string(def)); + + sc = seccomp_init(d); + if (!sc) + return json_log(v, flags, SYNTHETIC_ERRNO(ENOMEM), "Couldn't allocate seccomp object."); + + r = json_dispatch(v, table, oci_unexpected, flags, sc); + if (r < 0) + return r; + + seccomp_release(s->seccomp); + s->seccomp = TAKE_PTR(sc); + return 0; +#else + return json_log(v, flags, SYNTHETIC_ERRNO(EOPNOTSUPP), "libseccomp support not enabled, can't parse seccomp object."); +#endif +} + +static int oci_rootfs_propagation(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + const char *s; + + s = json_variant_string(v); + + if (streq(s, "shared")) + return 0; + + json_log(v, flags|JSON_DEBUG, 0, "Ignoring rootfsPropagation setting '%s'.", s); + return 0; +} + +static int oci_masked_paths(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + _cleanup_free_ char *destination = NULL; + CustomMount *m; + const char *p; + + if (!json_variant_is_string(e)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Path is not a string, refusing."); + + assert_se(p = json_variant_string(e)); + + if (!path_is_absolute(p)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Path is not not absolute, refusing: %s", p); + + if (oci_exclude_mount(p)) + continue; + + destination = strdup(p); + if (!destination) + return log_oom(); + + m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_INACCESSIBLE); + if (!m) + return log_oom(); + + m->destination = TAKE_PTR(destination); + + /* The spec doesn't say this, but apparently pre-existing implementations are lenient towards + * non-existing paths to mask. Let's hence be too. */ + m->graceful = true; + } + + return 0; +} + +static int oci_readonly_paths(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + _cleanup_free_ char *source = NULL, *destination = NULL; + CustomMount *m; + const char *p; + + if (!json_variant_is_string(e)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Path is not a string, refusing."); + + assert_se(p = json_variant_string(e)); + + if (!path_is_absolute(p)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Path is not not absolute, refusing: %s", p); + + if (oci_exclude_mount(p)) + continue; + + source = strjoin("+", p); + if (!source) + return log_oom(); + + destination = strdup(p); + if (!destination) + return log_oom(); + + m = custom_mount_add(&s->custom_mounts, &s->n_custom_mounts, CUSTOM_MOUNT_BIND); + if (!m) + return log_oom(); + + m->source = TAKE_PTR(source); + m->destination = TAKE_PTR(destination); + m->read_only = true; + } + + return 0; +} + +static int oci_linux(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "namespaces", JSON_VARIANT_ARRAY, oci_namespaces, 0, 0 }, + { "uidMappings", JSON_VARIANT_ARRAY, oci_uid_gid_mappings, 0, 0 }, + { "gidMappings", JSON_VARIANT_ARRAY, oci_uid_gid_mappings, 0, 0 }, + { "devices", JSON_VARIANT_ARRAY, oci_devices, 0, 0 }, + { "cgroupsPath", JSON_VARIANT_STRING, oci_cgroups_path, 0, 0 }, + { "resources", JSON_VARIANT_OBJECT, oci_resources, 0, 0 }, + { "intelRdt", JSON_VARIANT_OBJECT, oci_unsupported, 0, JSON_PERMISSIVE }, + { "sysctl", JSON_VARIANT_OBJECT, oci_sysctl, 0, 0 }, + { "seccomp", JSON_VARIANT_OBJECT, oci_seccomp, 0, 0 }, + { "rootfsPropagation", JSON_VARIANT_STRING, oci_rootfs_propagation, 0, 0 }, + { "maskedPaths", JSON_VARIANT_ARRAY, oci_masked_paths, 0, 0 }, + { "readonlyPaths", JSON_VARIANT_ARRAY, oci_readonly_paths, 0, 0 }, + { "mountLabel", JSON_VARIANT_STRING, oci_unsupported, 0, JSON_PERMISSIVE }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_hook_timeout(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + usec_t *u = userdata; + uintmax_t k; + + k = json_variant_unsigned(v); + if (k == 0) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Hook timeout cannot be zero."); + + if (k > (UINT64_MAX-1/USEC_PER_SEC)) + return json_log(v, flags, SYNTHETIC_ERRNO(EINVAL), + "Hook timeout too large."); + + *u = k * USEC_PER_SEC; + return 0; +} + +static int oci_hooks_array(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + Settings *s = userdata; + JsonVariant *e; + int r; + + assert(s); + + JSON_VARIANT_ARRAY_FOREACH(e, v) { + + static const JsonDispatch table[] = { + { "path", JSON_VARIANT_STRING, oci_absolute_path, offsetof(OciHook, path), JSON_MANDATORY }, + { "args", JSON_VARIANT_ARRAY, oci_args, offsetof(OciHook, args), 0 }, + { "env", JSON_VARIANT_ARRAY, oci_env, offsetof(OciHook, env), 0 }, + { "timeout", JSON_VARIANT_UNSIGNED, oci_hook_timeout, offsetof(OciHook, timeout), 0 }, + {} + }; + + OciHook *a, **array, *new_item; + size_t *n_array; + + if (streq(name, "prestart")) { + array = &s->oci_hooks_prestart; + n_array = &s->n_oci_hooks_prestart; + } else if (streq(name, "poststart")) { + array = &s->oci_hooks_poststart; + n_array = &s->n_oci_hooks_poststart; + } else { + assert(streq(name, "poststop")); + array = &s->oci_hooks_poststop; + n_array = &s->n_oci_hooks_poststop; + } + + a = reallocarray(*array, *n_array + 1, sizeof(OciHook)); + if (!a) + return log_oom(); + + *array = a; + new_item = a + *n_array; + + *new_item = (OciHook) { + .timeout = USEC_INFINITY, + }; + + r = json_dispatch(e, table, oci_unexpected, flags, userdata); + if (r < 0) { + free(new_item->path); + strv_free(new_item->args); + strv_free(new_item->env); + return r; + } + + (*n_array) ++; + } + + return 0; +} + +static int oci_hooks(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + + static const JsonDispatch table[] = { + { "prestart", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 }, + { "poststart", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 }, + { "poststop", JSON_VARIANT_OBJECT, oci_hooks_array, 0, 0 }, + {} + }; + + return json_dispatch(v, table, oci_unexpected, flags, userdata); +} + +static int oci_annotations(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) { + JsonVariant *k, *w; + + JSON_VARIANT_OBJECT_FOREACH(k, w, v) { + const char *n; + + assert_se(n = json_variant_string(k)); + + if (isempty(n)) + return json_log(k, flags, SYNTHETIC_ERRNO(EINVAL), + "Annotation with empty key, refusing."); + + if (!json_variant_is_string(w)) + return json_log(w, flags, SYNTHETIC_ERRNO(EINVAL), + "Annotation has non-string value, refusing."); + + json_log(k, flags|JSON_DEBUG, 0, "Ignoring annotation '%s' with value '%s'.", n, json_variant_string(w)); + } + + return 0; +} + +int oci_load(FILE *f, const char *bundle, Settings **ret) { + + static const JsonDispatch table[] = { + { "ociVersion", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY }, + { "process", JSON_VARIANT_OBJECT, oci_process, 0, 0 }, + { "root", JSON_VARIANT_OBJECT, oci_root, 0, 0 }, + { "hostname", JSON_VARIANT_STRING, oci_hostname, 0, 0 }, + { "mounts", JSON_VARIANT_ARRAY, oci_mounts, 0, 0 }, + { "linux", JSON_VARIANT_OBJECT, oci_linux, 0, 0 }, + { "hooks", JSON_VARIANT_OBJECT, oci_hooks, 0, 0 }, + { "annotations", JSON_VARIANT_OBJECT, oci_annotations, 0, 0 }, + {} + }; + + _cleanup_(json_variant_unrefp) JsonVariant *oci = NULL; + _cleanup_(settings_freep) Settings *s = NULL; + unsigned line = 0, column = 0; + JsonVariant *v; + const char *path; + int r; + + assert_se(bundle); + + path = strjoina(bundle, "/config.json"); + + r = json_parse_file(f, path, &oci, &line, &column); + if (r < 0) { + if (line != 0 && column != 0) + return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column); + else + return log_error_errno(r, "Failed to parse '%s': %m", path); + } + + v = json_variant_by_key(oci, "ociVersion"); + if (!v) { + log_error("JSON file '%s' is not an OCI bundle configuration file. Refusing.", path); + return -EINVAL; + } + if (!streq_ptr(json_variant_string(v), "1.0.0")) { + log_error("OCI bundle version not supported: %s", strna(json_variant_string(v))); + return -EINVAL; + } + + // { + // _cleanup_free_ char *formatted = NULL; + // assert_se(json_variant_format(oci, JSON_FORMAT_PRETTY|JSON_FORMAT_COLOR, &formatted) >= 0); + // fputs(formatted, stdout); + // } + + s = settings_new(); + if (!s) + return log_oom(); + + s->start_mode = START_PID1; + s->resolv_conf = RESOLV_CONF_OFF; + s->link_journal = LINK_NO; + s->timezone = TIMEZONE_OFF; + + s->bundle = strdup(bundle); + if (!s->bundle) + return log_oom(); + + r = json_dispatch(oci, table, oci_unexpected, 0, s); + if (r < 0) + return r; + + if (s->properties) { + r = sd_bus_message_seal(s->properties, 0, 0); + if (r < 0) + return log_error_errno(r, "Cannot seal properties bus message: %m"); + } + + *ret = TAKE_PTR(s); + return 0; +} diff --git a/src/nspawn/nspawn-oci.h b/src/nspawn/nspawn-oci.h new file mode 100644 index 000000000..d23a2f3da --- /dev/null +++ b/src/nspawn/nspawn-oci.h @@ -0,0 +1,6 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "nspawn-settings.h" + +int oci_load(FILE *f, const char *path, Settings **ret); diff --git a/src/nspawn/nspawn-patch-uid.c b/src/nspawn/nspawn-patch-uid.c index db2f5467b..c2b779c1d 100644 --- a/src/nspawn/nspawn-patch-uid.c +++ b/src/nspawn/nspawn-patch-uid.c @@ -402,7 +402,7 @@ read_only: /* When we hit a ready-only subtree we simply skip it, but log about it. */ (void) fd_get_path(fd, &name); - log_debug("Skippping read-only file or directory %s.", strna(name)); + log_debug("Skipping read-only file or directory %s.", strna(name)); r = changed; } diff --git a/src/nspawn/nspawn-register.c b/src/nspawn/nspawn-register.c index a7cdfc1c7..8e2c32966 100644 --- a/src/nspawn/nspawn-register.c +++ b/src/nspawn/nspawn-register.c @@ -5,6 +5,7 @@ #include "bus-error.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "bus-wait-for-jobs.h" #include "nspawn-register.h" #include "special.h" #include "stat-util.h" @@ -111,6 +112,7 @@ int register_machine( unsigned n_mounts, int kill_signal, char **properties, + sd_bus_message *properties_message, bool keep_unit, const char *service) { @@ -184,6 +186,12 @@ int register_machine( if (r < 0) return r; + if (properties_message) { + r = sd_bus_message_copy(m, properties_message, true); + if (r < 0) + return bus_log_create_error(r); + } + r = bus_append_unit_property_assignment_many(m, UNIT_SERVICE, properties); if (r < 0) return r; @@ -234,7 +242,8 @@ int allocate_scope( CustomMount *mounts, unsigned n_mounts, int kill_signal, - char **properties) { + char **properties, + sd_bus_message *properties_message) { _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; @@ -288,6 +297,12 @@ int allocate_scope( if (r < 0) return r; + if (properties_message) { + r = sd_bus_message_copy(m, properties_message, true); + if (r < 0) + return bus_log_create_error(r); + } + r = append_machine_properties( m, mounts, diff --git a/src/nspawn/nspawn-register.h b/src/nspawn/nspawn-register.h index 05f5776f2..65a3ae85a 100644 --- a/src/nspawn/nspawn-register.h +++ b/src/nspawn/nspawn-register.h @@ -7,8 +7,8 @@ #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 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, sd_bus_message *properties_message, bool keep_unit, const char *service); 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 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, sd_bus_message *properties_message); 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 e7ef80f7d..9b5eb011a 100644 --- a/src/nspawn/nspawn-seccomp.c +++ b/src/nspawn/nspawn-seccomp.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #if HAVE_SECCOMP diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c index 505e8cac4..476cb0779 100644 --- a/src/nspawn/nspawn-settings.c +++ b/src/nspawn/nspawn-settings.c @@ -17,6 +17,50 @@ #include "user-util.h" #include "util.h" +Settings *settings_new(void) { + Settings *s; + + s = new(Settings, 1); + if (!s) + return NULL; + + *s = (Settings) { + .start_mode = _START_MODE_INVALID, + .personality = PERSONALITY_INVALID, + + .resolv_conf = _RESOLV_CONF_MODE_INVALID, + .link_journal = _LINK_JOURNAL_INVALID, + .timezone = _TIMEZONE_MODE_INVALID, + + .userns_mode = _USER_NAMESPACE_MODE_INVALID, + .userns_chown = -1, + .uid_shift = UID_INVALID, + .uid_range = UID_INVALID, + + .no_new_privileges = -1, + + .read_only = -1, + .volatile_mode = _VOLATILE_MODE_INVALID, + + .private_network = -1, + .network_veth = -1, + + .full_capabilities = CAPABILITY_QUINTET_NULL, + + .uid = UID_INVALID, + .gid = GID_INVALID, + + .console_mode = _CONSOLE_MODE_INVALID, + .console_width = (unsigned) -1, + .console_height = (unsigned) -1, + + .clone_ns_flags = (unsigned long) -1, + .use_cgns = -1, + }; + + return s; +} + int settings_load(FILE *f, const char *path, Settings **ret) { _cleanup_(settings_freep) Settings *s = NULL; int r; @@ -24,27 +68,10 @@ int settings_load(FILE *f, const char *path, Settings **ret) { assert(path); assert(ret); - s = new0(Settings, 1); + s = settings_new(); if (!s) return -ENOMEM; - s->start_mode = _START_MODE_INVALID; - s->personality = PERSONALITY_INVALID; - s->userns_mode = _USER_NAMESPACE_MODE_INVALID; - s->resolv_conf = _RESOLV_CONF_MODE_INVALID; - s->link_journal = _LINK_JOURNAL_INVALID; - s->timezone = _TIMEZONE_MODE_INVALID; - s->uid_shift = UID_INVALID; - s->uid_range = UID_INVALID; - s->no_new_privileges = -1; - - s->read_only = -1; - s->volatile_mode = _VOLATILE_MODE_INVALID; - s->userns_chown = -1; - - s->private_network = -1; - s->network_veth = -1; - r = config_parse(NULL, path, f, "Exec\0" "Network\0" @@ -66,12 +93,33 @@ int settings_load(FILE *f, const char *path, Settings **ret) { s->userns_mode = USER_NAMESPACE_NO; *ret = TAKE_PTR(s); - return 0; } -Settings* settings_free(Settings *s) { +static void free_oci_hooks(OciHook *h, size_t n) { + size_t i; + assert(h || n == 0); + + for (i = 0; i < n; i++) { + free(h[i].path); + strv_free(h[i].args); + strv_free(h[i].env); + } + + free(h); +} + +void device_node_array_free(DeviceNode *node, size_t n) { + size_t i; + + for (i = 0; i < n; i++) + free(node[i].path); + + free(node); +} + +Settings* settings_free(Settings *s) { if (!s) return NULL; @@ -96,6 +144,27 @@ Settings* settings_free(Settings *s) { expose_port_free_all(s->expose_ports); custom_mount_free_all(s->custom_mounts, s->n_custom_mounts); + + free(s->bundle); + free(s->root); + + free_oci_hooks(s->oci_hooks_prestart, s->n_oci_hooks_prestart); + free_oci_hooks(s->oci_hooks_poststart, s->n_oci_hooks_poststart); + free_oci_hooks(s->oci_hooks_poststop, s->n_oci_hooks_poststop); + + free(s->slice); + sd_bus_message_unref(s->properties); + + free(s->supplementary_gids); + device_node_array_free(s->extra_nodes, s->n_extra_nodes); + free(s->network_namespace_path); + + strv_free(s->sysctl); + +#if HAVE_SECCOMP + seccomp_release(s->seccomp); +#endif + return mfree(s); } @@ -122,6 +191,26 @@ bool settings_network_veth(Settings *s) { s->network_zone; } +int settings_allocate_properties(Settings *s) { + _cleanup_(sd_bus_unrefp) sd_bus *bus = NULL; + int r; + + assert(s); + + if (s->properties) + return 0; + + r = sd_bus_default_system(&bus); + if (r < 0) + return r; + + r = sd_bus_message_new(bus, &s->properties, SD_BUS_MESSAGE_METHOD_CALL); + if (r < 0) + return r; + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_volatile_mode, volatile_mode, VolatileMode, "Failed to parse volatile mode"); int config_parse_expose_port( @@ -315,6 +404,34 @@ int config_parse_tmpfs( return 0; } +int config_parse_inaccessible( + 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) { + + Settings *settings = data; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = inaccessible_mount_parse(&settings->custom_mounts, &settings->n_custom_mounts, rvalue); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, "Invalid inaccessible file system specification %s: %m", rvalue); + return 0; + } + + return 0; +} + int config_parse_overlay( const char *unit, const char *filename, diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h index a63aa32e9..64910c3ec 100644 --- a/src/nspawn/nspawn-settings.h +++ b/src/nspawn/nspawn-settings.h @@ -4,13 +4,20 @@ #include #include +#if HAVE_SECCOMP +#include +#endif + +#include "sd-bus.h" #include "sd-id128.h" +#include "capability-util.h" #include "conf-parser.h" #include "macro.h" #include "missing_resource.h" #include "nspawn-expose-ports.h" #include "nspawn-mount.h" +#include "time-util.h" typedef enum StartMode { START_PID1, /* Run parameters as command line as process 1 */ @@ -60,6 +67,15 @@ typedef enum TimezoneMode { _TIMEZONE_MODE_INVALID = -1 } TimezoneMode; +typedef enum ConsoleMode { + CONSOLE_INTERACTIVE, + CONSOLE_READ_ONLY, + CONSOLE_PASSIVE, + CONSOLE_PIPE, + _CONSOLE_MODE_MAX, + _CONSOLE_MODE_INVALID = -1, +} ConsoleMode; + typedef enum SettingsMask { SETTING_START_MODE = UINT64_C(1) << 0, SETTING_ENVIRONMENT = UINT64_C(1) << 1, @@ -86,9 +102,14 @@ typedef enum SettingsMask { SETTING_LINK_JOURNAL = UINT64_C(1) << 22, SETTING_TIMEZONE = UINT64_C(1) << 23, 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_SLICE = UINT64_C(1) << 25, + SETTING_DIRECTORY = UINT64_C(1) << 26, + SETTING_USE_CGNS = UINT64_C(1) << 27, + SETTING_CLONE_NS_FLAGS = UINT64_C(1) << 28, + SETTING_CONSOLE_MODE = UINT64_C(1) << 29, + SETTING_RLIMIT_FIRST = UINT64_C(1) << 30, /* we define one bit per resource limit here */ + SETTING_RLIMIT_LAST = UINT64_C(1) << (30 + _RLIMIT_MAX - 1), + _SETTINGS_MASK_ALL = (UINT64_C(1) << (30 + _RLIMIT_MAX)) -1, _SETTING_FORCE_ENUM_WIDTH = UINT64_MAX } SettingsMask; @@ -101,6 +122,22 @@ assert_cc(sizeof(SettingsMask) == 8); assert_cc(sizeof(SETTING_RLIMIT_FIRST) == 8); assert_cc(sizeof(SETTING_RLIMIT_LAST) == 8); +typedef struct DeviceNode { + char *path; + unsigned major; + unsigned minor; + mode_t mode; + uid_t uid; + gid_t gid; +} DeviceNode; + +typedef struct OciHook { + char *path; + char **args; + char **env; + usec_t timeout; +} OciHook; + typedef struct Settings { /* [Run] */ StartMode start_mode; @@ -150,13 +187,39 @@ typedef struct Settings { char **network_ipvlan; char **network_veth_extra; ExposePort *expose_ports; + + /* Additional fields, that are specific to OCI runtime case */ + char *bundle; + char *root; + OciHook *oci_hooks_prestart, *oci_hooks_poststart, *oci_hooks_poststop; + size_t n_oci_hooks_prestart, n_oci_hooks_poststart, n_oci_hooks_poststop; + char *slice; + sd_bus_message *properties; + CapabilityQuintet full_capabilities; + uid_t uid; + gid_t gid; + gid_t *supplementary_gids; + size_t n_supplementary_gids; + unsigned console_width, console_height; + ConsoleMode console_mode; + DeviceNode *extra_nodes; + size_t n_extra_nodes; + unsigned long clone_ns_flags; + char *network_namespace_path; + int use_cgns; + char **sysctl; +#if HAVE_SECCOMP + scmp_filter_ctx seccomp; +#endif } Settings; +Settings *settings_new(void); int settings_load(FILE *f, const char *path, Settings **ret); Settings* settings_free(Settings *s); bool settings_network_veth(Settings *s); bool settings_private_network(Settings *s); +int settings_allocate_properties(Settings *s); DEFINE_TRIVIAL_CLEANUP_FUNC(Settings*, settings_free); @@ -170,6 +233,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_pivot_root); CONFIG_PARSER_PROTOTYPE(config_parse_bind); CONFIG_PARSER_PROTOTYPE(config_parse_tmpfs); CONFIG_PARSER_PROTOTYPE(config_parse_overlay); +CONFIG_PARSER_PROTOTYPE(config_parse_inaccessible); CONFIG_PARSER_PROTOTYPE(config_parse_veth_extra); CONFIG_PARSER_PROTOTYPE(config_parse_network_zone); CONFIG_PARSER_PROTOTYPE(config_parse_boot); @@ -190,3 +254,5 @@ const char *timezone_mode_to_string(TimezoneMode a) _const_; TimezoneMode timezone_mode_from_string(const char *s) _pure_; int parse_link_journal(const char *s, LinkJournal *ret_mode, bool *ret_try); + +void device_node_array_free(DeviceNode *node, size_t n); diff --git a/src/nspawn/nspawn-setuid.c b/src/nspawn/nspawn-setuid.c index 0026e4e3f..3c302d648 100644 --- a/src/nspawn/nspawn-setuid.c +++ b/src/nspawn/nspawn-setuid.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include #include @@ -43,7 +44,7 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { if (rearrange_stdio(-1, pipe_fds[1], -1) < 0) _exit(EXIT_FAILURE); - close_all_fds(NULL, 0); + (void) close_all_fds(NULL, 0); (void) rlimit_nofile_safe(); @@ -59,14 +60,41 @@ static int spawn_getent(const char *database, const char *key, pid_t *rpid) { return pipe_fds[0]; } +int change_uid_gid_raw( + uid_t uid, + gid_t gid, + const gid_t *supplementary_gids, + size_t n_supplementary_gids) { + + if (!uid_is_valid(uid)) + uid = 0; + if (!gid_is_valid(gid)) + gid = 0; + + (void) fchown(STDIN_FILENO, uid, gid); + (void) fchown(STDOUT_FILENO, uid, gid); + (void) fchown(STDERR_FILENO, uid, gid); + + if (setgroups(n_supplementary_gids, supplementary_gids) < 0) + return log_error_errno(errno, "Failed to set auxiliary groups: %m"); + + if (setresgid(gid, gid, gid) < 0) + return log_error_errno(errno, "setresgid() failed: %m"); + + if (setresuid(uid, uid, uid) < 0) + return log_error_errno(errno, "setresuid() failed: %m"); + + return 0; +} + int change_uid_gid(const char *user, char **_home) { char *x, *u, *g, *h; const char *word, *state; - _cleanup_free_ uid_t *uids = NULL; + _cleanup_free_ gid_t *gids = NULL; _cleanup_free_ char *home = NULL, *line = NULL; _cleanup_fclose_ FILE *f = NULL; _cleanup_close_ int fd = -1; - unsigned n_uids = 0; + unsigned n_gids = 0; size_t sz = 0, l; uid_t uid; gid_t gid; @@ -189,10 +217,10 @@ int change_uid_gid(const char *user, char **_home) { memcpy(c, word, l); c[l] = 0; - if (!GREEDY_REALLOC(uids, sz, n_uids+1)) + if (!GREEDY_REALLOC(gids, sz, n_gids+1)) return log_oom(); - r = parse_uid(c, &uids[n_uids++]); + r = parse_gid(c, &gids[n_gids++]); if (r < 0) return log_error_errno(r, "Failed to parse group data from getent: %m"); } @@ -205,18 +233,9 @@ int change_uid_gid(const char *user, char **_home) { if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR)) return log_error_errno(r, "Failed to make home directory: %m"); - (void) fchown(STDIN_FILENO, uid, gid); - (void) fchown(STDOUT_FILENO, uid, gid); - (void) fchown(STDERR_FILENO, uid, gid); - - if (setgroups(n_uids, uids) < 0) - return log_error_errno(errno, "Failed to set auxiliary groups: %m"); - - if (setresgid(gid, gid, gid) < 0) - return log_error_errno(errno, "setresgid() failed: %m"); - - if (setresuid(uid, uid, uid) < 0) - return log_error_errno(errno, "setresuid() failed: %m"); + r = change_uid_gid_raw(uid, gid, gids, n_gids); + if (r < 0) + return r; if (_home) *_home = TAKE_PTR(home); diff --git a/src/nspawn/nspawn-setuid.h b/src/nspawn/nspawn-setuid.h index 0ae47cb6b..9a2b05ebb 100644 --- a/src/nspawn/nspawn-setuid.h +++ b/src/nspawn/nspawn-setuid.h @@ -1,4 +1,5 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -int change_uid_gid(const char *user, char **ret); +int change_uid_gid_raw(uid_t uid, gid_t gid, const gid_t *supplementary_gids, size_t n_supplementary_gids); +int change_uid_gid(const char *user, char **ret_home); diff --git a/src/nspawn/nspawn-stub-pid1.c b/src/nspawn/nspawn-stub-pid1.c index 5d17df39d..ebf4f0f52 100644 --- a/src/nspawn/nspawn-stub-pid1.c +++ b/src/nspawn/nspawn-stub-pid1.c @@ -67,7 +67,7 @@ int stub_pid1(sd_id128_t uuid) { reset_all_signal_handlers(); log_close(); - close_all_fds(NULL, 0); + (void) close_all_fds(NULL, 0); log_open(); /* Flush out /proc/self/environ, so that we don't leak the environment from the host into the container. Also, diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index e0c2d711e..3b0ecb1db 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -57,22 +57,26 @@ #include "loopback-setup.h" #include "machine-image.h" #include "macro.h" +#include "main-func.h" #include "missing.h" #include "mkdir.h" #include "mount-util.h" #include "mountpoint-util.h" +#include "namespace-util.h" #include "netlink-util.h" #include "nspawn-cgroup.h" #include "nspawn-def.h" #include "nspawn-expose-ports.h" #include "nspawn-mount.h" #include "nspawn-network.h" +#include "nspawn-oci.h" #include "nspawn-patch-uid.h" #include "nspawn-register.h" #include "nspawn-seccomp.h" #include "nspawn-settings.h" #include "nspawn-setuid.h" #include "nspawn-stub-pid1.h" +#include "nulstr-util.h" #include "os-util.h" #include "pager.h" #include "parse-util.h" @@ -84,6 +88,9 @@ #include "raw-clone.h" #include "rlimit-util.h" #include "rm-rf.h" +#if HAVE_SECCOMP +#include "seccomp-util.h" +#endif #include "selinux-util.h" #include "signal-util.h" #include "socket-util.h" @@ -92,6 +99,7 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" +#include "sysctl-util.h" #include "terminal-util.h" #include "tmpfile-util.h" #include "umask-util.h" @@ -113,7 +121,7 @@ typedef enum ContainerStatus { CONTAINER_TERMINATED, - CONTAINER_REBOOTED + CONTAINER_REBOOTED, } ContainerStatus; static char *arg_directory = NULL; @@ -122,12 +130,16 @@ static char *arg_chdir = NULL; static char *arg_pivot_root_new = NULL; static char *arg_pivot_root_old = NULL; static char *arg_user = NULL; +static uid_t arg_uid = UID_INVALID; +static gid_t arg_gid = GID_INVALID; +static gid_t* arg_supplementary_gids = NULL; +static size_t arg_n_supplementary_gids = 0; static sd_id128_t arg_uuid = {}; static char *arg_machine = NULL; /* The name used by the host to refer to this */ static char *arg_hostname = NULL; /* The name the payload sees by default */ static const char *arg_selinux_context = NULL; static const char *arg_selinux_apifs_context = NULL; -static const char *arg_slice = NULL; +static char *arg_slice = NULL; static bool arg_private_network = false; static bool arg_read_only = false; static StartMode arg_start_mode = START_PID1; @@ -161,6 +173,7 @@ static uint64_t arg_caps_retain = (1ULL << CAP_SYS_PTRACE) | (1ULL << CAP_SYS_RESOURCE) | (1ULL << CAP_SYS_TTY_CONFIG); +static CapabilityQuintet arg_full_capabilities = CAPABILITY_QUINTET_NULL; static CustomMount *arg_custom_mounts = NULL; static size_t arg_n_custom_mounts = 0; static char **arg_setenv = NULL; @@ -175,11 +188,14 @@ static char **arg_network_veth_extra = NULL; static char *arg_network_bridge = NULL; static char *arg_network_zone = NULL; static char *arg_network_namespace_path = NULL; +static PagerFlags arg_pager_flags = 0; static unsigned long arg_personality = PERSONALITY_INVALID; static char *arg_image = NULL; +static char *arg_oci_bundle = NULL; static VolatileMode arg_volatile_mode = VOLATILE_NO; static ExposePort *arg_expose_ports = NULL; static char **arg_property = NULL; +static sd_bus_message *arg_property_message = NULL; static UserNamespaceMode arg_userns_mode = USER_NAMESPACE_NO; static uid_t arg_uid_shift = UID_INVALID, arg_uid_range = 0x10000U; static bool arg_userns_chown = false; @@ -197,6 +213,9 @@ static void *arg_root_hash = NULL; static size_t arg_root_hash_size = 0; static char **arg_syscall_whitelist = NULL; static char **arg_syscall_blacklist = NULL; +#if HAVE_SECCOMP +static scmp_filter_ctx arg_seccomp = NULL; +#endif static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {}; static bool arg_no_new_privileges = false; static int arg_oom_score_adjust = 0; @@ -205,44 +224,99 @@ static cpu_set_t *arg_cpuset = NULL; static unsigned arg_cpuset_ncpus = 0; static ResolvConfMode arg_resolv_conf = RESOLV_CONF_AUTO; static TimezoneMode arg_timezone = TIMEZONE_AUTO; +static unsigned arg_console_width = (unsigned) -1, arg_console_height = (unsigned) -1; +static DeviceNode* arg_extra_nodes = NULL; +static size_t arg_n_extra_nodes = 0; +static char **arg_sysctl = NULL; +static ConsoleMode arg_console_mode = _CONSOLE_MODE_INVALID; + +STATIC_DESTRUCTOR_REGISTER(arg_directory, freep); +STATIC_DESTRUCTOR_REGISTER(arg_template, freep); +STATIC_DESTRUCTOR_REGISTER(arg_chdir, freep); +STATIC_DESTRUCTOR_REGISTER(arg_pivot_root_new, freep); +STATIC_DESTRUCTOR_REGISTER(arg_pivot_root_old, freep); +STATIC_DESTRUCTOR_REGISTER(arg_user, freep); +STATIC_DESTRUCTOR_REGISTER(arg_supplementary_gids, freep); +STATIC_DESTRUCTOR_REGISTER(arg_machine, freep); +STATIC_DESTRUCTOR_REGISTER(arg_hostname, freep); +STATIC_DESTRUCTOR_REGISTER(arg_slice, freep); +STATIC_DESTRUCTOR_REGISTER(arg_setenv, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_interfaces, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_macvlan, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_ipvlan, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_veth_extra, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_bridge, freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_zone, freep); +STATIC_DESTRUCTOR_REGISTER(arg_network_namespace_path, freep); +STATIC_DESTRUCTOR_REGISTER(arg_image, freep); +STATIC_DESTRUCTOR_REGISTER(arg_oci_bundle, freep); +STATIC_DESTRUCTOR_REGISTER(arg_property, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_property_message, sd_bus_message_unrefp); +STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_root_hash, freep); +STATIC_DESTRUCTOR_REGISTER(arg_syscall_whitelist, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_syscall_blacklist, strv_freep); +#if HAVE_SECCOMP +STATIC_DESTRUCTOR_REGISTER(arg_seccomp, seccomp_releasep); +#endif +STATIC_DESTRUCTOR_REGISTER(arg_cpuset, CPU_FREEp); +STATIC_DESTRUCTOR_REGISTER(arg_sysctl, strv_freep); static int help(void) { _cleanup_free_ char *link = NULL; int r; - (void) pager_open(false); + (void) pager_open(arg_pager_flags); r = terminal_urlify_man("systemd-nspawn", "1", &link); if (r < 0) return log_oom(); - printf("%s [OPTIONS...] [PATH] [ARGUMENTS...]\n\n" + printf("%1$s [OPTIONS...] [PATH] [ARGUMENTS...]\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" + " --no-pager Do not pipe output into a pager\n" + " --settings=BOOLEAN Load additional settings from .nspawn file\n\n" + "%3$sImage:%4$s\n" " -D --directory=PATH Root directory for the container\n" " --template=PATH Initialize root directory from template directory,\n" " if missing\n" " -x --ephemeral Run container with snapshot of root directory, and\n" " remove it after exit\n" - " -i --image=PATH File system device or disk image for the container\n" - " --root-hash=HASH Specify verity root hash\n" + " -i --image=PATH Root file system disk image (or device node) for\n" + " the container\n" + " --oci-bundle=PATH OCI bundle directory\n" + " --read-only Mount the root directory read-only\n" + " --volatile[=MODE] Run the system in volatile mode\n" + " --root-hash=HASH Specify verity root hash for root disk image\n" + " --pivot-root=PATH[:PATH]\n" + " Pivot root to given directory in the container\n\n" + "%3$sExecution:%4$s\n" " -a --as-pid2 Maintain a stub init as PID1, invoke binary as PID2\n" " -b --boot Boot up full system (i.e. invoke init)\n" " --chdir=PATH Set working directory in the container\n" - " --pivot-root=PATH[:PATH]\n" - " Pivot root to given directory in the container\n" - " -u --user=USER Run the command under specified user or uid\n" + " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" + " -u --user=USER Run the command under specified user or UID\n" + " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" + " --notify-ready=BOOLEAN Receive notifications from the child init process\n\n" + "%3$sSystem Identity:%4$s\n" " -M --machine=NAME Set the machine name for the container\n" " --hostname=NAME Override the hostname for the container\n" - " --uuid=UUID Set a specific machine UUID for the container\n" + " --uuid=UUID Set a specific machine UUID for the container\n\n" + "%3$sProperties:%4$s\n" " -S --slice=SLICE Place the container in the specified slice\n" " --property=NAME=VALUE Set scope unit property\n" + " --register=BOOLEAN Register container as machine\n" + " --keep-unit Do not register a scope for the machine, reuse\n" + " the service unit nspawn is running in\n\n" + "%3$sUser Namespacing:%4$s\n" " -U --private-users=pick Run within user namespace, autoselect UID/GID range\n" " --private-users[=UIDBASE[:NUIDS]]\n" " Similar, but with user configured UID/GID range\n" - " --private-users-chown Adjust OS tree ownership to private UID/GID range\n" + " --private-users-chown Adjust OS tree ownership to private UID/GID range\n\n" + "%3$sNetworking:%4$s\n" " --private-network Disable network in container\n" " --network-interface=INTERFACE\n" " Assign an existing network interface to the\n" @@ -267,51 +341,54 @@ static int help(void) { " Set network namespace to the one represented by\n" " the specified kernel namespace file node\n" " -p --port=[PROTOCOL:]HOSTPORT[:CONTAINERPORT]\n" - " Expose a container IP port on the host\n" + " Expose a container IP port on the host\n\n" + "%3$sSecurity:%4$s\n" + " --capability=CAP In addition to the default, retain specified\n" + " capability\n" + " --drop-capability=CAP Drop the specified capability from the default set\n" + " --no-new-privileges Set PR_SET_NO_NEW_PRIVS flag for container payload\n" + " --system-call-filter=LIST|~LIST\n" + " Permit/prohibit specific system calls\n" " -Z --selinux-context=SECLABEL\n" " Set the SELinux security context to be used by\n" " processes in the container\n" " -L --selinux-apifs-context=SECLABEL\n" " Set the SELinux security context to be used by\n" - " API/tmpfs file systems in the container\n" - " --capability=CAP In addition to the default, retain specified\n" - " capability\n" - " --drop-capability=CAP Drop the specified capability from the default set\n" - " --system-call-filter=LIST|~LIST\n" - " Permit/prohibit specific system calls\n" + " API/tmpfs file systems in the container\n\n" + "%3$sResources:%4$s\n" " --rlimit=NAME=LIMIT Set a resource limit for the payload\n" " --oom-score-adjust=VALUE\n" " Adjust the OOM score value for the payload\n" " --cpu-affinity=CPUS Adjust the CPU affinity of the container\n" - " --kill-signal=SIGNAL Select signal to use for shutting down PID 1\n" - " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" - " host, try-guest, try-host\n" - " -j Equivalent to --link-journal=try-guest\n" + " --personality=ARCH Pick personality for this container\n\n" + "%3$sIntegration:%4$s\n" " --resolv-conf=MODE Select mode of /etc/resolv.conf initialization\n" " --timezone=MODE Select mode of /etc/localtime initialization\n" - " --read-only Mount the root directory read-only\n" + " --link-journal=MODE Link up guest journal, one of no, auto, guest, \n" + " host, try-guest, try-host\n" + " -j Equivalent to --link-journal=try-guest\n\n" + "%3$sMounts:%4$s\n" " --bind=PATH[:PATH[:OPTIONS]]\n" " Bind mount a file or directory from the host into\n" " the container\n" " --bind-ro=PATH[:PATH[:OPTIONS]\n" " Similar, but creates a read-only bind mount\n" + " --inaccessible=PATH Over-mount file node with inaccessible node to mask\n" + " it\n" " --tmpfs=PATH:[OPTIONS] Mount an empty tmpfs to the specified directory\n" " --overlay=PATH[:PATH...]:PATH\n" " Create an overlay mount from the host to \n" " the container\n" " --overlay-ro=PATH[:PATH...]:PATH\n" - " Similar, but creates a read-only overlay mount\n" - " -E --setenv=NAME=VALUE Pass an environment variable to PID 1\n" - " --register=BOOLEAN Register container as machine\n" - " --keep-unit Do not register a scope for the machine, reuse\n" - " the service unit nspawn is running in\n" - " --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" - "\nSee the %s for details.\n" + " Similar, but creates a read-only overlay mount\n\n" + "%3$sInput/Output:%4$s\n" + " --console=MODE Select how stdin/stdout/stderr and /dev/console are\n" + " set up for the container.\n" + " -P --pipe Equivalent to --console=pipe\n" + "\nSee the %2$s for details.\n" , program_invocation_short_name , link - ); + , ansi_underline(), ansi_normal()); return 0; } @@ -399,7 +476,9 @@ static void parse_share_ns_env(const char *name, unsigned long ns_flag) { return; if (r < 0) log_warning_errno(r, "Failed to parse %s from environment, defaulting to false.", name); + arg_clone_ns_flags = (arg_clone_ns_flags & ~ns_flag) | (r > 0 ? 0 : ns_flag); + arg_settings_mask |= SETTING_CLONE_NS_FLAGS; } static void parse_mount_settings_env(void) { @@ -444,9 +523,20 @@ static void parse_environment(void) { /* 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()) + if (!cg_ns_supported()) arg_use_cgns = false; + else { + r = getenv_bool("SYSTEMD_NSPAWN_USE_CGNS"); + if (r < 0) { + if (r != -ENXIO) + log_warning_errno(r, "Failed to parse $SYSTEMD_NSPAWN_USE_CGNS, ignoring: %m"); + + arg_use_cgns = true; + } else { + arg_use_cgns = r > 0; + arg_settings_mask |= SETTING_USE_CGNS; + } + } e = getenv("SYSTEMD_NSPAWN_CONTAINER_SERVICE"); if (e) @@ -469,6 +559,7 @@ static int parse_argv(int argc, char *argv[]) { ARG_TMPFS, ARG_OVERLAY, ARG_OVERLAY_RO, + ARG_INACCESSIBLE, ARG_SHARE_SYSTEM, ARG_REGISTER, ARG_KEEP_UNIT, @@ -499,6 +590,10 @@ static int parse_argv(int argc, char *argv[]) { ARG_CPU_AFFINITY, ARG_RESOLV_CONF, ARG_TIMEZONE, + ARG_CONSOLE, + ARG_PIPE, + ARG_OCI_BUNDLE, + ARG_NO_PAGER, }; static const struct option options[] = { @@ -522,6 +617,7 @@ static int parse_argv(int argc, char *argv[]) { { "tmpfs", required_argument, NULL, ARG_TMPFS }, { "overlay", required_argument, NULL, ARG_OVERLAY }, { "overlay-ro", required_argument, NULL, ARG_OVERLAY_RO }, + { "inaccessible", required_argument, NULL, ARG_INACCESSIBLE }, { "machine", required_argument, NULL, 'M' }, { "hostname", required_argument, NULL, ARG_HOSTNAME }, { "slice", required_argument, NULL, 'S' }, @@ -559,6 +655,10 @@ static int parse_argv(int argc, char *argv[]) { { "cpu-affinity", required_argument, NULL, ARG_CPU_AFFINITY }, { "resolv-conf", required_argument, NULL, ARG_RESOLV_CONF }, { "timezone", required_argument, NULL, ARG_TIMEZONE }, + { "console", required_argument, NULL, ARG_CONSOLE }, + { "pipe", no_argument, NULL, ARG_PIPE }, + { "oci-bundle", required_argument, NULL, ARG_OCI_BUNDLE }, + { "no-pager", no_argument, NULL, ARG_NO_PAGER }, {} }; @@ -570,7 +670,7 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nUE:", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "+hD:u:abL:M:jS:Z:qi:xp:nUE:P", options, NULL)) >= 0) switch (c) { case 'h': @@ -583,18 +683,31 @@ static int parse_argv(int argc, char *argv[]) { r = parse_path_argument_and_warn(optarg, false, &arg_directory); if (r < 0) return r; + + arg_settings_mask |= SETTING_DIRECTORY; break; case ARG_TEMPLATE: r = parse_path_argument_and_warn(optarg, false, &arg_template); if (r < 0) return r; + + arg_settings_mask |= SETTING_DIRECTORY; break; case 'i': r = parse_path_argument_and_warn(optarg, false, &arg_image); if (r < 0) return r; + + arg_settings_mask |= SETTING_DIRECTORY; + break; + + case ARG_OCI_BUNDLE: + r = parse_path_argument_and_warn(optarg, false, &arg_oci_bundle); + if (r < 0) + return r; + break; case 'x': @@ -702,6 +815,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; + arg_settings_mask |= SETTING_NETWORK; break; case 'b': @@ -735,7 +849,11 @@ static int parse_argv(int argc, char *argv[]) { break; case 'S': - arg_slice = optarg; + r = free_and_strdup(&arg_slice, optarg); + if (r < 0) + return log_oom(); + + arg_settings_mask |= SETTING_SLICE; break; case 'M': @@ -790,7 +908,6 @@ static int parse_argv(int argc, char *argv[]) { r = extract_first_word(&p, &t, ",", 0); if (r < 0) return log_error_errno(r, "Failed to parse capability %s.", t); - if (r == 0) break; @@ -832,10 +949,8 @@ static int parse_argv(int argc, char *argv[]) { case ARG_LINK_JOURNAL: r = parse_link_journal(optarg, &arg_link_journal, &arg_link_journal_try); - if (r < 0) { - log_error_errno(r, "Failed to parse link journal mode %s", optarg); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "Failed to parse link journal mode %s", optarg); arg_settings_mask |= SETTING_LINK_JOURNAL; break; @@ -868,6 +983,14 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_CUSTOM_MOUNTS; break; + case ARG_INACCESSIBLE: + r = inaccessible_mount_parse(&arg_custom_mounts, &arg_n_custom_mounts, optarg); + if (r < 0) + return log_error_errno(r, "Failed to parse --inaccessible= argument %s: %m", optarg); + + arg_settings_mask |= SETTING_CUSTOM_MOUNTS; + break; + case 'E': { char **n; @@ -1118,9 +1241,8 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return log_error_errno(r, "Failed to parse root hash: %s", optarg); if (l < sizeof(sd_id128_t)) { - log_error("Root hash must be at least 128bit long: %s", optarg); free(k); - return -EINVAL; + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Root hash must be at least 128bit long: %s", optarg); } free(arg_root_hash); @@ -1250,6 +1372,36 @@ static int parse_argv(int argc, char *argv[]) { arg_settings_mask |= SETTING_TIMEZONE; break; + case ARG_CONSOLE: + if (streq(optarg, "interactive")) + arg_console_mode = CONSOLE_INTERACTIVE; + else if (streq(optarg, "read-only")) + arg_console_mode = CONSOLE_READ_ONLY; + else if (streq(optarg, "passive")) + arg_console_mode = CONSOLE_PASSIVE; + else if (streq(optarg, "pipe")) + arg_console_mode = CONSOLE_PIPE; + else if (streq(optarg, "help")) + puts("interactive\n" + "read-only\n" + "passive\n" + "pipe"); + else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown console mode: %s", optarg); + + arg_settings_mask |= SETTING_CONSOLE_MODE; + break; + + case 'P': + case ARG_PIPE: + arg_console_mode = CONSOLE_PIPE; + arg_settings_mask |= SETTING_CONSOLE_MODE; + break; + + case ARG_NO_PAGER: + arg_pager_flags |= PAGER_DISABLE; + break; + case '?': return -EINVAL; @@ -1273,7 +1425,10 @@ static int parse_argv(int argc, char *argv[]) { * --directory=". */ arg_directory = TAKE_PTR(arg_template); - arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? 1ULL << CAP_NET_ADMIN : 0)) & ~minus; + arg_caps_retain = (arg_caps_retain | plus | (arg_private_network ? UINT64_C(1) << CAP_NET_ADMIN : 0)) & ~minus; + + /* Make sure to parse environment before we reset the settings mask below */ + parse_environment(); /* Load all settings from .nspawn files */ if (mask_no_settings) @@ -1308,6 +1463,9 @@ static int verify_arguments(void) { if (arg_start_mode == START_BOOT && arg_kill_signal <= 0) arg_kill_signal = SIGRTMIN+3; + if (arg_volatile_mode != VOLATILE_NO) /* Make sure all file systems contained in the image are mounted read-only if we are in volatile mode */ + arg_read_only = true; + 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. */ @@ -1332,19 +1490,27 @@ static int verify_arguments(void) { return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "--private-users= is not supported, kernel compiled without user namespace support."); if (arg_userns_chown && arg_read_only) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--read-only and --private-users-chown may not be combined."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--read-only and --private-users-chown may not be combined."); - /* If --network-namespace-path is given with any other network-related option, - * we need to error out, to avoid conflicts between different network options. */ + /* We don't support --private-users-chown together with any of the volatile modes since we couldn't + * change the read-only part of the tree (i.e. /usr) anyway, or because it would trigger a massive + * copy-up (in case of overlay) making the entire excercise pointless. */ + if (arg_userns_chown && arg_volatile_mode != VOLATILE_NO) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--volatile= and --private-users-chown may not be combined."); + + /* 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."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--network-namespace-path= cannot be combined with other network options."); if (arg_network_bridge && arg_network_zone) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--network-bridge= and --network-zone= may not be combined."); + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--network-bridge= and --network-zone= may not be combined."); 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."); @@ -1352,9 +1518,6 @@ static int verify_arguments(void) { 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."); - 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."); - if (arg_expose_ports && !arg_private_network) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --port= without private networking."); @@ -1420,6 +1583,10 @@ static const char *timezone_from_path(const char *path) { "/usr/share/zoneinfo/"); } +static bool etc_writable(void) { + return !arg_read_only || IN_SET(arg_volatile_mode, VOLATILE_YES, VOLATILE_OVERLAY); +} + static int setup_timezone(const char *dest) { _cleanup_free_ char *p = NULL, *etc = NULL; const char *where, *check; @@ -1431,9 +1598,9 @@ static int setup_timezone(const char *dest) { if (IN_SET(arg_timezone, TIMEZONE_AUTO, TIMEZONE_SYMLINK)) { r = readlink_malloc("/etc/localtime", &p); if (r == -ENOENT && arg_timezone == TIMEZONE_AUTO) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? TIMEZONE_OFF : TIMEZONE_DELETE; + m = etc_writable() ? TIMEZONE_DELETE : TIMEZONE_OFF; else if (r == -EINVAL && arg_timezone == TIMEZONE_AUTO) /* regular file? */ - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? TIMEZONE_BIND : TIMEZONE_COPY; + m = etc_writable() ? TIMEZONE_COPY : TIMEZONE_BIND; else if (r < 0) { log_warning_errno(r, "Failed to read host's /etc/localtime symlink, not updating container timezone: %m"); /* To handle warning, delete /etc/localtime and replace it with a symbolic link to a time zone data @@ -1444,7 +1611,7 @@ static int setup_timezone(const char *dest) { */ return 0; } else if (arg_timezone == TIMEZONE_AUTO) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? TIMEZONE_BIND : TIMEZONE_SYMLINK; + m = etc_writable() ? TIMEZONE_SYMLINK : TIMEZONE_BIND; else m = arg_timezone; } else @@ -1529,7 +1696,7 @@ static int setup_timezone(const char *dest) { case TIMEZONE_COPY: /* If mounting failed, try to copy */ - r = copy_file_atomic("/etc/localtime", where, 0644, 0, COPY_REFLINK|COPY_REPLACE); + r = copy_file_atomic("/etc/localtime", where, 0644, 0, 0, COPY_REFLINK|COPY_REPLACE); if (r < 0) { log_full_errno(IN_SET(r, -EROFS, -EACCES, -EPERM) ? LOG_DEBUG : LOG_WARNING, r, "Failed to copy /etc/localtime to %s, ignoring: %m", where); @@ -1606,11 +1773,11 @@ 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) - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_BIND_STATIC : RESOLV_CONF_COPY_STATIC; + m = etc_writable() ? RESOLV_CONF_COPY_STATIC : RESOLV_CONF_BIND_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; + m = etc_writable() ? RESOLV_CONF_COPY_HOST : RESOLV_CONF_BIND_HOST; else - m = arg_read_only && arg_volatile_mode != VOLATILE_YES ? RESOLV_CONF_OFF : RESOLV_CONF_DELETE; + m = etc_writable() ? RESOLV_CONF_DELETE : RESOLV_CONF_OFF; } else m = arg_resolv_conf; @@ -1656,7 +1823,7 @@ static int setup_resolv_conf(const char *dest) { } /* If that didn't work, let's copy the file */ - r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, COPY_REFLINK); + r = copy_file(what, where, O_TRUNC|O_NOFOLLOW, 0644, 0, 0, COPY_REFLINK); if (r < 0) { /* If the file already exists as symlink, let's suppress the warning, under the assumption that * resolved or something similar runs inside and the symlink points there. @@ -1682,10 +1849,9 @@ static int setup_boot_id(void) { const char *to; int r; - /* Generate a new randomized boot ID, so that each boot-up of - * the container gets a new one */ + /* Generate a new randomized boot ID, so that each boot-up of the container gets a new one */ - r = tempfn_random_child(NULL, "proc-sys-kernel-random-boot-id", &path); + r = tempfn_random_child("/run", "proc-sys-kernel-random-boot-id", &path); if (r < 0) return log_error_errno(r, "Failed to generate random boot ID path: %m"); @@ -1717,9 +1883,9 @@ static int copy_devnodes(const char *dest) { "tty\0" "net/tun\0"; + _cleanup_umask_ mode_t u; const char *d; int r = 0; - _cleanup_umask_ mode_t u; assert(dest); @@ -1799,6 +1965,32 @@ static int copy_devnodes(const char *dest) { return r; } +static int make_extra_nodes(const char *dest) { + _cleanup_umask_ mode_t u; + size_t i; + int r; + + u = umask(0000); + + for (i = 0; i < arg_n_extra_nodes; i++) { + _cleanup_free_ char *path = NULL; + DeviceNode *n = arg_extra_nodes + i; + + path = prefix_root(dest, n->path); + if (!path) + return log_oom(); + + if (mknod(path, n->mode, S_ISCHR(n->mode) || S_ISBLK(n->mode) ? makedev(n->major, n->minor) : 0) < 0) + return log_error_errno(errno, "Failed to create device node '%s': %m", path); + + r = chmod_and_chown(path, n->mode, n->uid, n->gid); + if (r < 0) + return log_error_errno(r, "Failed to adjust device node ownership of '%s': %m", path); + } + + return 0; +} + static int setup_pts(const char *dest) { _cleanup_free_ char *options = NULL; const char *p; @@ -1855,10 +2047,12 @@ static int setup_dev_console(const char *dest, const char *console) { int r; assert(dest); - assert(console); u = umask(0000); + if (!console) + return 0; + r = chmod_and_chown(console, 0600, arg_uid_shift, arg_uid_shift); if (r < 0) return log_error_errno(r, "Failed to correct access mode for TTY: %m"); @@ -1901,19 +2095,18 @@ static int setup_kmsg(int kmsg_socket) { _cleanup_free_ char *fifo = NULL; _cleanup_close_ int fd = -1; _cleanup_umask_ mode_t u; - const char *to; int r; assert(kmsg_socket >= 0); u = umask(0000); - /* We create the kmsg FIFO as as temporary file in /tmp, but immediately delete it after bind mounting it to + /* We create the kmsg FIFO as as temporary file in /run, but immediately delete it after bind mounting it to * /proc/kmsg. While FIFOs on the reading side behave very similar to /proc/kmsg, their writing side behaves * differently from /dev/kmsg in that writing blocks when nothing is reading. In order to avoid any problems * with containers deadlocking due to this we simply make /dev/kmsg unavailable to the container. */ - r = tempfn_random_child(NULL, "proc-kmsg", &fifo); + r = tempfn_random_child("/run", "proc-kmsg", &fifo); if (r < 0) return log_error_errno(r, "Failed to generate kmsg path: %m"); @@ -1921,9 +2114,8 @@ static int setup_kmsg(int kmsg_socket) { return log_error_errno(errno, "mkfifo() for /run/kmsg failed: %m"); from = TAKE_PTR(fifo); - to = "/proc/kmsg"; - r = mount_verbose(LOG_ERR, from, to, NULL, MS_BIND, NULL); + r = mount_verbose(LOG_ERR, from, "/proc/kmsg", NULL, MS_BIND, NULL); if (r < 0) return r; @@ -2096,8 +2288,40 @@ static int setup_journal(const char *directory) { return 0; } -static int drop_capabilities(void) { - return capability_bounding_set_drop(arg_caps_retain, false); +static int drop_capabilities(uid_t uid) { + CapabilityQuintet q; + + /* Let's initialize all five capability sets to something valid. If the quintet was configured via + * OCI use that, but fill in missing bits. If it wasn't then derive the quintet in full from + * arg_caps_retain. */ + + if (capability_quintet_is_set(&arg_full_capabilities)) { + q = arg_full_capabilities; + + if (q.bounding == (uint64_t) -1) + q.bounding = uid == 0 ? arg_caps_retain : 0; + + if (q.effective == (uint64_t) -1) + q.effective = uid == 0 ? q.bounding : 0; + + if (q.inheritable == (uint64_t) -1) + q.inheritable = uid == 0 ? q.bounding : 0; + + if (q.permitted == (uint64_t) -1) + q.permitted = uid == 0 ? q.bounding : 0; + + if (q.ambient == (uint64_t) -1 && ambient_capabilities_supported()) + q.ambient = 0; + } else + q = (CapabilityQuintet) { + .bounding = arg_caps_retain, + .effective = uid == 0 ? arg_caps_retain : 0, + .inheritable = uid == 0 ? arg_caps_retain : 0, + .permitted = uid == 0 ? arg_caps_retain : 0, + .ambient = ambient_capabilities_supported() ? 0 : (uint64_t) -1, + }; + + return capability_quintet_enforce(&q); } static int reset_audit_loginuid(void) { @@ -2386,10 +2610,8 @@ static int determine_names(void) { return log_error_errno(r, "Failed to determine current directory: %m"); } - if (!arg_directory && !arg_image) { - log_error("Failed to determine path, please use -D or -i."); - return -EINVAL; - } + if (!arg_directory && !arg_image) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine path, please use -D or -i."); } if (!arg_machine) { @@ -2412,10 +2634,8 @@ static int determine_names(void) { return log_oom(); hostname_cleanup(arg_machine); - if (!machine_name_is_valid(arg_machine)) { - log_error("Failed to determine machine name automatically, please use -M."); - return -EINVAL; - } + if (!machine_name_is_valid(arg_machine)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Failed to determine machine name automatically, please use -M."); if (arg_ephemeral) { char *b; @@ -2484,6 +2704,75 @@ static int determine_uid_shift(const char *directory) { return 0; } +static unsigned long effective_clone_ns_flags(void) { + unsigned long flags = arg_clone_ns_flags; + + if (arg_private_network) + flags |= CLONE_NEWNET; + if (arg_use_cgns) + flags |= CLONE_NEWCGROUP; + if (arg_userns_mode != USER_NAMESPACE_NO) + flags |= CLONE_NEWUSER; + + return flags; +} + +static int patch_sysctl(void) { + + /* This table is inspired by runc's sysctl() function */ + static const struct { + const char *key; + bool prefix; + unsigned long clone_flags; + } safe_sysctl[] = { + { "kernel.hostname", false, CLONE_NEWUTS }, + { "kernel.domainname", false, CLONE_NEWUTS }, + { "kernel.msgmax", false, CLONE_NEWIPC }, + { "kernel.msgmnb", false, CLONE_NEWIPC }, + { "kernel.msgmni", false, CLONE_NEWIPC }, + { "kernel.sem", false, CLONE_NEWIPC }, + { "kernel.shmall", false, CLONE_NEWIPC }, + { "kernel.shmmax", false, CLONE_NEWIPC }, + { "kernel.shmmni", false, CLONE_NEWIPC }, + { "fs.mqueue.", true, CLONE_NEWIPC }, + { "net.", true, CLONE_NEWNET }, + }; + + unsigned long flags; + char **k, **v; + int r; + + flags = effective_clone_ns_flags(); + + STRV_FOREACH_PAIR(k, v, arg_sysctl) { + bool good = false; + size_t i; + + for (i = 0; i < ELEMENTSOF(safe_sysctl); i++) { + + if (!FLAGS_SET(flags, safe_sysctl[i].clone_flags)) + continue; + + if (safe_sysctl[i].prefix) + good = startswith(*k, safe_sysctl[i].key); + else + good = streq(*k, safe_sysctl[i].key); + + if (good) + break; + } + + if (!good) + return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Refusing to write to sysctl '%s', as it is not safe in the selected namespaces.", *k); + + r = sysctl_write(*k, *v); + if (r < 0) + return log_error_errno(r, "Failed to write sysctl '%s': %m", *k); + } + + return 0; +} + static int inner_child( Barrier *barrier, const char *directory, @@ -2510,7 +2799,7 @@ static int inner_child( }; const char *exec_target; _cleanup_strv_free_ char **env_use = NULL; - int r; + int r, which_failed; /* 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, @@ -2527,6 +2816,8 @@ static int inner_child( assert(directory); assert(kmsg_socket >= 0); + log_debug("Inner child is initializing."); + if (arg_userns_mode != USER_NAMESPACE_NO) { /* Tell the parent, that it now can write the UID map. */ (void) barrier_place(barrier); /* #1 */ @@ -2596,6 +2887,18 @@ static int inner_child( return r; kmsg_socket = safe_close(kmsg_socket); + r = mount_custom( + "/", + arg_custom_mounts, + arg_n_custom_mounts, + false, + 0, + 0, + arg_selinux_apifs_context, + true); + if (r < 0) + return r; + if (setsid() < 0) return log_error_errno(errno, "setsid() failed: %m"); @@ -2609,6 +2912,10 @@ static int inner_child( rtnl_socket = safe_close(rtnl_socket); } + r = patch_sysctl(); + if (r < 0) + return r; + if (arg_oom_score_adjust_set) { r = set_oom_score_adjust(arg_oom_score_adjust); if (r < 0) @@ -2619,10 +2926,6 @@ static int inner_child( if (sched_setaffinity(0, CPU_ALLOC_SIZE(arg_cpuset_ncpus), arg_cpuset) < 0) return log_error_errno(errno, "Failed to set CPU affinity: %m"); - r = drop_capabilities(); - if (r < 0) - return log_error_errno(r, "drop_capabilities() failed: %m"); - (void) setup_hostname(); if (arg_personality != PERSONALITY_INVALID) { @@ -2635,16 +2938,51 @@ static int inner_child( return log_error_errno(r, "personality() failed: %m"); } + r = setrlimit_closest_all((const struct rlimit *const*) arg_rlimit, &which_failed); + if (r < 0) + return log_error_errno(r, "Failed to apply resource limit RLIMIT_%s: %m", rlimit_to_string(which_failed)); + +#if HAVE_SECCOMP + if (arg_seccomp) { + + if (is_seccomp_available()) { + + r = seccomp_load(arg_seccomp); + if (IN_SET(r, -EPERM, -EACCES)) + return log_error_errno(r, "Failed to install seccomp filter: %m"); + if (r < 0) + log_debug_errno(r, "Failed to install seccomp filter: %m"); + } + } else +#endif + { + r = setup_seccomp(arg_caps_retain, arg_syscall_whitelist, arg_syscall_blacklist); + if (r < 0) + return r; + } + #if HAVE_SELINUX if (arg_selinux_context) if (setexeccon(arg_selinux_context) < 0) return log_error_errno(errno, "setexeccon(\"%s\") failed: %m", arg_selinux_context); #endif - r = change_uid_gid(arg_user, &home); + /* Make sure we keep the caps across the uid/gid dropping, so that we can retain some selected caps + * if we need to later on. */ + if (prctl(PR_SET_KEEPCAPS, 1) < 0) + return log_error_errno(errno, "Failed to set PR_SET_KEEPCAPS: %m"); + + if (uid_is_valid(arg_uid) || gid_is_valid(arg_gid)) + r = change_uid_gid_raw(arg_uid, arg_gid, arg_supplementary_gids, arg_n_supplementary_gids); + else + r = change_uid_gid(arg_user, &home); if (r < 0) return r; + r = drop_capabilities(getuid()); + if (r < 0) + return log_error_errno(r, "Dropping capabilities failed: %m"); + if (arg_no_new_privileges) if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) return log_error_errno(errno, "Failed to disable new privileges: %m"); @@ -2656,10 +2994,14 @@ static int inner_child( if (envp[n_env]) n_env++; - if ((asprintf((char**)(envp + n_env++), "HOME=%s", home ? home: "/root") < 0) || - (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ? arg_user : "root") < 0) || - (asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0)) - return log_oom(); + if (home || !uid_is_valid(arg_uid) || arg_uid == 0) + if (asprintf((char**)(envp + n_env++), "HOME=%s", home ?: "/root") < 0) + return log_oom(); + + if (arg_user || !uid_is_valid(arg_uid) || arg_uid == 0) + if (asprintf((char**)(envp + n_env++), "USER=%s", arg_user ?: "root") < 0 || + asprintf((char**)(envp + n_env++), "LOGNAME=%s", arg_user ? arg_user : "root") < 0) + return log_oom(); assert(!sd_id128_is_null(arg_uuid)); @@ -2699,6 +3041,8 @@ static int inner_child( return r; } + log_debug("Inner child completed, invoking payload."); + /* Now, explicitly close the log, so that we then can close all remaining fds. Closing the log explicitly first * has the benefit that the logging subsystem knows about it, and is thus ready to be reopened should we need * it again. Note that the other fds closed here are at least the locking and barrier fds. */ @@ -2791,7 +3135,6 @@ static int outer_child( const char *directory, const char *console, DissectedImage *dissected_image, - bool interactive, bool secondary, int pid_socket, int uuid_socket, @@ -2804,9 +3147,9 @@ static int outer_child( int netns_fd) { _cleanup_close_ int fd = -1; - int r, which_failed; pid_t pid; ssize_t l; + int r; /* 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, @@ -2815,18 +3158,21 @@ static int outer_child( assert(barrier); assert(directory); - assert(console); assert(pid_socket >= 0); assert(uuid_socket >= 0); assert(notify_socket >= 0); assert(kmsg_socket >= 0); + log_debug("Outer child is initializing."); + if (prctl(PR_SET_PDEATHSIG, SIGKILL) < 0) return log_error_errno(errno, "PR_SET_PDEATHSIG failed: %m"); - if (interactive) { + if (arg_console_mode != CONSOLE_PIPE) { int terminal; + assert(console); + terminal = open_terminal(console, O_RDWR); if (terminal < 0) return log_error_errno(terminal, "Failed to open console: %m"); @@ -2896,6 +3242,30 @@ static int outer_child( "Selected user namespace base " UID_FMT " and range " UID_FMT ".", arg_uid_shift, arg_uid_range); } + if (!dissected_image) { + /* Turn directory into bind mount */ + r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL); + if (r < 0) + return r; + } + + r = setup_pivot_root( + directory, + arg_pivot_root_new, + arg_pivot_root_old); + if (r < 0) + return r; + + r = setup_volatile_mode( + directory, + arg_volatile_mode, + arg_userns_mode != USER_NAMESPACE_NO, + arg_uid_shift, + arg_uid_range, + arg_selinux_context); + if (r < 0) + return r; + if (dissected_image) { /* Now we know the uid shift, let's now mount everything else that might be in the image. */ r = dissected_image_mount(dissected_image, directory, arg_uid_shift, @@ -2921,38 +3291,6 @@ static int outer_child( unified_cgroup_hierarchy_socket = safe_close(unified_cgroup_hierarchy_socket); } - /* Turn directory into bind mount */ - r = mount_verbose(LOG_ERR, directory, directory, NULL, MS_BIND|MS_REC, NULL); - if (r < 0) - return r; - - r = setup_pivot_root( - directory, - arg_pivot_root_new, - arg_pivot_root_old); - if (r < 0) - return r; - - r = setup_volatile( - directory, - arg_volatile_mode, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_context); - if (r < 0) - return r; - - r = setup_volatile_state( - directory, - arg_volatile_mode, - arg_userns_mode != USER_NAMESPACE_NO, - arg_uid_shift, - arg_uid_range, - arg_selinux_context); - if (r < 0) - return r; - /* Mark everything as shared so our mounts get propagated down. This is * required to make new bind mounts available in systemd services * inside the containter that create a new mount namespace. @@ -2971,8 +3309,8 @@ static int outer_child( if (r < 0) return r; - if (arg_read_only) { - r = bind_remount_recursive(directory, true, NULL); + if (arg_read_only && arg_volatile_mode == VOLATILE_NO) { + r = bind_remount_recursive(directory, MS_RDONLY, MS_RDONLY, NULL); if (r < 0) return log_error_errno(r, "Failed to make tree read-only: %m"); } @@ -2988,7 +3326,12 @@ static int outer_child( if (r < 0) return r; - dev_setup(directory, arg_uid_shift, arg_uid_shift); + r = make_extra_nodes(directory); + if (r < 0) + return r; + + (void) dev_setup(directory, arg_uid_shift, arg_uid_shift); + (void) make_inaccessible_nodes(directory, arg_uid_shift, arg_uid_shift); r = setup_pts(directory); if (r < 0) @@ -3006,10 +3349,6 @@ static int outer_child( if (r < 0) return r; - r = setup_seccomp(arg_caps_retain, arg_syscall_whitelist, arg_syscall_blacklist); - if (r < 0) - return r; - r = setup_timezone(directory); if (r < 0) return r; @@ -3033,7 +3372,8 @@ static int outer_child( arg_userns_mode != USER_NAMESPACE_NO, arg_uid_shift, arg_uid_range, - arg_selinux_apifs_context); + arg_selinux_apifs_context, + false); if (r < 0) return r; @@ -3058,10 +3398,6 @@ static int outer_child( if (fd < 0) return fd; - r = setrlimit_closest_all((const struct rlimit *const*) arg_rlimit, &which_failed); - if (r < 0) - return log_error_errno(r, "Failed to apply resource limit RLIMIT_%s: %m", rlimit_to_string(which_failed)); - pid = raw_clone(SIGCHLD|CLONE_NEWNS| arg_clone_ns_flags | (arg_userns_mode != USER_NAMESPACE_NO ? CLONE_NEWUSER : 0)); @@ -3275,11 +3611,11 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r return log_oom(); if (strv_find(tags, "READY=1")) - sd_notifyf(false, "READY=1\n"); + (void) sd_notifyf(false, "READY=1\n"); p = strv_find_startswith(tags, "STATUS="); if (p) - sd_notifyf(false, "STATUS=Container running: %s", p); + (void) sd_notifyf(false, "STATUS=Container running: %s", p); return 0; } @@ -3314,6 +3650,15 @@ static int merge_settings(Settings *settings, const char *path) { if ((arg_settings_mask & SETTING_EPHEMERAL) == 0) arg_ephemeral = settings->ephemeral; + if ((arg_settings_mask & SETTING_DIRECTORY) == 0 && + settings->root) { + + if (!arg_settings_trusted) + log_warning("Ignoring root directory setting, file %s is not trusted.", path); + else + free_and_replace(arg_directory, settings->root); + } + if ((arg_settings_mask & SETTING_PIVOT_ROOT) == 0 && settings->pivot_root_new) { free_and_replace(arg_pivot_root_new, settings->pivot_root_new); @@ -3328,16 +3673,36 @@ static int merge_settings(Settings *settings, const char *path) { settings->environment) strv_free_and_replace(arg_setenv, settings->environment); - if ((arg_settings_mask & SETTING_USER) == 0 && - settings->user) - free_and_replace(arg_user, settings->user); + if ((arg_settings_mask & SETTING_USER) == 0) { + + if (settings->user) + free_and_replace(arg_user, settings->user); + + if (uid_is_valid(settings->uid)) + arg_uid = settings->uid; + if (gid_is_valid(settings->gid)) + arg_gid = settings->gid; + if (settings->n_supplementary_gids > 0) { + free_and_replace(arg_supplementary_gids, settings->supplementary_gids); + arg_n_supplementary_gids = settings->n_supplementary_gids; + } + } if ((arg_settings_mask & SETTING_CAPABILITY) == 0) { - uint64_t plus; + uint64_t plus, minus; + + /* Note that we copy both the simple plus/minus caps here, and the full quintet from the + * Settings structure */ plus = settings->capability; - if (settings_private_network(settings)) - plus |= (1ULL << CAP_NET_ADMIN); + minus = settings->drop_capability; + + if ((arg_settings_mask & SETTING_NETWORK) == 0) { + if (settings_private_network(settings)) + plus |= UINT64_C(1) << CAP_NET_ADMIN; + else + minus |= UINT64_C(1) << CAP_NET_ADMIN; + } if (!arg_settings_trusted && plus != 0) { if (settings->capability != 0) @@ -3345,7 +3710,15 @@ static int merge_settings(Settings *settings, const char *path) { } else arg_caps_retain |= plus; - arg_caps_retain &= ~settings->drop_capability; + arg_caps_retain &= ~minus; + + /* Copy the full capabilities over too */ + if (capability_quintet_is_set(&settings->full_capabilities)) { + if (!arg_settings_trusted) + log_warning("Ignoring capabilitiy settings, file %s is not trusted.", path); + else + arg_full_capabilities = settings->full_capabilities; + } } if ((arg_settings_mask & SETTING_KILL_SIGNAL) == 0 && @@ -3394,7 +3767,8 @@ static int merge_settings(Settings *settings, const char *path) { settings->network_interfaces || settings->network_macvlan || settings->network_ipvlan || - settings->network_veth_extra)) { + settings->network_veth_extra || + settings->network_namespace_path)) { if (!arg_settings_trusted) log_warning("Ignoring network settings, file %s is not trusted.", path); @@ -3409,6 +3783,8 @@ static int merge_settings(Settings *settings, const char *path) { free_and_replace(arg_network_bridge, settings->network_bridge); free_and_replace(arg_network_zone, settings->network_zone); + + free_and_replace(arg_network_namespace_path, settings->network_namespace_path); } } @@ -3441,12 +3817,21 @@ static int merge_settings(Settings *settings, const char *path) { if ((arg_settings_mask & SETTING_SYSCALL_FILTER) == 0) { - if (!arg_settings_trusted && !strv_isempty(arg_syscall_whitelist)) + if (!arg_settings_trusted && !strv_isempty(settings->syscall_whitelist)) log_warning("Ignoring SystemCallFilter= settings, file %s is not trusted.", path); else { strv_free_and_replace(arg_syscall_whitelist, settings->syscall_whitelist); strv_free_and_replace(arg_syscall_blacklist, settings->syscall_blacklist); } + +#if HAVE_SECCOMP + if (!arg_settings_trusted && settings->seccomp) + log_warning("Ignoring SECCOMP filter, file %s is not trusted.", path); + else { + seccomp_release(arg_seccomp); + arg_seccomp = TAKE_PTR(settings->seccomp); + } +#endif } for (rl = 0; rl < _RLIMIT_MAX; rl ++) { @@ -3515,6 +3900,55 @@ static int merge_settings(Settings *settings, const char *path) { settings->timezone != _TIMEZONE_MODE_INVALID) arg_timezone = settings->timezone; + if ((arg_settings_mask & SETTING_SLICE) == 0 && + settings->slice) { + + if (!arg_settings_trusted) + log_warning("Ignoring slice setting, file '%s' is not trusted.", path); + else + free_and_replace(arg_slice, settings->slice); + } + + if ((arg_settings_mask & SETTING_USE_CGNS) == 0 && + settings->use_cgns >= 0) { + + if (!arg_settings_trusted) + log_warning("Ignoring cgroup namespace setting, file '%s' is not trusted.", path); + else + arg_use_cgns = settings->use_cgns; + } + + if ((arg_settings_mask & SETTING_CLONE_NS_FLAGS) == 0 && + settings->clone_ns_flags != (unsigned long) -1) { + + if (!arg_settings_trusted) + log_warning("Ignoring namespace setting, file '%s' is not trusted.", path); + else + arg_clone_ns_flags = settings->clone_ns_flags; + } + + if ((arg_settings_mask & SETTING_CONSOLE_MODE) == 0 && + settings->console_mode >= 0) { + + if (!arg_settings_trusted) + log_warning("Ignoring console mode setting, file '%s' is not trusted.", path); + else + arg_console_mode = settings->console_mode; + } + + /* The following properties can only be set through the OCI settings logic, not from the command line, hence we + * don't consult arg_settings_mask for them. */ + + sd_bus_message_unref(arg_property_message); + arg_property_message = TAKE_PTR(settings->properties); + + arg_console_width = settings->console_width; + arg_console_height = settings->console_height; + + device_node_array_free(arg_extra_nodes, arg_n_extra_nodes); + arg_extra_nodes = TAKE_PTR(settings->extra_nodes); + arg_n_extra_nodes = settings->n_extra_nodes; + return 0; } @@ -3525,6 +3959,9 @@ static int load_settings(void) { const char *fn, *i; int r; + if (arg_oci_bundle) + return 0; + /* If all settings are masked, there's no point in looking for * the settings file */ if ((arg_settings_mask & _SETTINGS_MASK_ALL) == _SETTINGS_MASK_ALL) @@ -3592,10 +4029,27 @@ static int load_settings(void) { return merge_settings(settings, p); } -static int run(int master, +static int load_oci_bundle(void) { + _cleanup_(settings_freep) Settings *settings = NULL; + int r; + + if (!arg_oci_bundle) + return 0; + + /* By default let's trust OCI bundles */ + if (arg_settings_trusted < 0) + arg_settings_trusted = true; + + r = oci_load(NULL, arg_oci_bundle, &settings); + if (r < 0) + return r; + + return merge_settings(settings, arg_oci_bundle); +} + +static int run_container(int master, const char* console, DissectedImage *dissected_image, - bool interactive, bool secondary, FDSet *fds, char veth_name[IFNAMSIZ], bool *veth_created, @@ -3626,7 +4080,6 @@ static int run(int master, _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; ContainerStatus container_status = 0; - char last_char = 0; int ifi = 0, r; ssize_t l; sigset_t mask_chld; @@ -3695,10 +4148,9 @@ static int run(int master, 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); - else if (r == 0) { - log_error("Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path); - return -EINVAL; - } + else if (r == 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Path %s doesn't refer to a network namespace, refusing.", arg_network_namespace_path); } *pid = raw_clone(SIGCHLD|CLONE_NEWNS); @@ -3728,7 +4180,6 @@ static int run(int master, arg_directory, console, dissected_image, - interactive, secondary, pid_socket_pair[1], uuid_socket_pair[1], @@ -3747,7 +4198,7 @@ static int run(int master, barrier_set_role(&barrier, BARRIER_PARENT); - fds = fdset_free(fds); + fdset_close(fds); kmsg_socket_pair[1] = safe_close(kmsg_socket_pair[1]); rtnl_socket_pair[1] = safe_close(rtnl_socket_pair[1]); @@ -3762,10 +4213,8 @@ static int run(int master, l = recv(uid_shift_socket_pair[0], &arg_uid_shift, sizeof arg_uid_shift, 0); if (l < 0) return log_error_errno(errno, "Failed to read UID shift: %m"); - if (l != sizeof arg_uid_shift) { - log_error("Short read while reading UID shift."); - return -EIO; - } + if (l != sizeof arg_uid_shift) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading UID shift."); if (arg_userns_mode == USER_NAMESPACE_PICK) { /* If we are supposed to pick the UID shift, let's try to use the shift read from the @@ -3779,10 +4228,8 @@ static int run(int master, l = send(uid_shift_socket_pair[0], &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 writing UID shift."); - return -EIO; - } + if (l != sizeof arg_uid_shift) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short write while writing UID shift."); } } @@ -3791,11 +4238,9 @@ static int run(int master, l = recv(unified_cgroup_hierarchy_socket_pair[0], &arg_unified_cgroup_hierarchy, sizeof(arg_unified_cgroup_hierarchy), 0); 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 (%zu bytes).%s", - l, l == 0 ? " The child is most likely dead." : ""); - return -EIO; - } + if (l != sizeof(arg_unified_cgroup_hierarchy)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading cgroup mode (%zu bytes).%s", + l, l == 0 ? " The child is most likely dead." : ""); } /* Wait for the outer child. */ @@ -3809,19 +4254,15 @@ static int run(int master, l = recv(pid_socket_pair[0], pid, sizeof *pid, 0); if (l < 0) return log_error_errno(errno, "Failed to read inner child PID: %m"); - if (l != sizeof *pid) { - log_error("Short read while reading inner child PID."); - return -EIO; - } + if (l != sizeof *pid) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading inner child PID."); /* We also retrieve container UUID in case it was generated by outer child */ l = recv(uuid_socket_pair[0], &arg_uuid, sizeof arg_uuid, 0); if (l < 0) return log_error_errno(errno, "Failed to read container machine ID: %m"); - if (l != sizeof(arg_uuid)) { - log_error("Short read while reading container machined ID."); - return -EIO; - } + if (l != sizeof(arg_uuid)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading container machined ID."); /* We also retrieve the socket used for notifications generated by outer child */ notify_socket = receive_one_fd(notify_socket_pair[0], 0); @@ -3832,10 +4273,8 @@ static int run(int master, log_debug("Init process invoked as PID "PID_FMT, *pid); if (arg_userns_mode != USER_NAMESPACE_NO) { - if (!barrier_place_and_sync(&barrier)) { /* #1 */ - log_error("Child died too early."); - return -ESRCH; - } + if (!barrier_place_and_sync(&barrier)) /* #1 */ + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early."); r = setup_uid_map(*pid); if (r < 0) @@ -3847,10 +4286,8 @@ static int run(int master, if (arg_private_network) { if (!arg_network_namespace_path) { /* Wait until the child has unshared its network namespace. */ - if (!barrier_place_and_sync(&barrier)) { /* #3 */ - log_error("Child died too early"); - return -ESRCH; - } + if (!barrier_place_and_sync(&barrier)) /* #3 */ + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early"); } r = move_network_interfaces(*pid, arg_network_interfaces); @@ -3940,6 +4377,7 @@ static int run(int master, arg_custom_mounts, arg_n_custom_mounts, arg_kill_signal, arg_property, + arg_property_message, arg_keep_unit, arg_container_service_name); if (r < 0) @@ -3953,18 +4391,19 @@ static int run(int master, arg_slice, arg_custom_mounts, arg_n_custom_mounts, arg_kill_signal, - arg_property); + arg_property, + arg_property_message); if (r < 0) return r; } else if (arg_slice || arg_property) log_notice("Machine and scope registration turned off, --slice= and --property= settings will have no effect."); - r = sync_cgroup(*pid, arg_unified_cgroup_hierarchy, arg_uid_shift); + r = create_subcgroup(*pid, arg_keep_unit, arg_unified_cgroup_hierarchy); if (r < 0) return r; - r = create_subcgroup(*pid, arg_keep_unit, arg_unified_cgroup_hierarchy); + r = sync_cgroup(*pid, arg_unified_cgroup_hierarchy, arg_uid_shift); if (r < 0) return r; @@ -4004,18 +4443,16 @@ static int run(int master, return r; /* Let the child know that we are ready and wait that the child is completely ready now. */ - if (!barrier_place_and_sync(&barrier)) { /* #5 */ - log_error("Child died too early."); - return -ESRCH; - } + if (!barrier_place_and_sync(&barrier)) /* #5 */ + return log_error_errno(SYNTHETIC_ERRNO(ESRCH), "Child died too early."); /* At this point we have made use of the UID we picked, and thus nss-mymachines * will make them appear in getpwuid(), thus we can release the /etc/passwd lock. */ etc_passwd_lock = safe_close(etc_passwd_lock); - sd_notifyf(false, - "STATUS=Container running.\n" - "X_NSPAWN_LEADER_PID=" PID_FMT, *pid); + (void) sd_notifyf(false, + "STATUS=Container running.\n" + "X_NSPAWN_LEADER_PID=" PID_FMT, *pid); if (!arg_notify_ready) (void) sd_notify(false, "READY=1\n"); @@ -4042,22 +4479,32 @@ static int run(int master, rtnl_socket_pair[0] = safe_close(rtnl_socket_pair[0]); - r = pty_forward_new(event, master, - PTY_FORWARD_IGNORE_VHANGUP | (interactive ? 0 : PTY_FORWARD_READ_ONLY), - &forward); - if (r < 0) - return log_error_errno(r, "Failed to create PTY forwarder: %m"); + if (IN_SET(arg_console_mode, CONSOLE_INTERACTIVE, CONSOLE_READ_ONLY)) { + assert(master >= 0); + + r = pty_forward_new(event, master, + PTY_FORWARD_IGNORE_VHANGUP | (arg_console_mode == CONSOLE_READ_ONLY ? PTY_FORWARD_READ_ONLY : 0), + &forward); + if (r < 0) + return log_error_errno(r, "Failed to create PTY forwarder: %m"); + + if (arg_console_width != (unsigned) -1 || arg_console_height != (unsigned) -1) + (void) pty_forward_set_width_height(forward, arg_console_width, arg_console_height); + } r = sd_event_loop(event); if (r < 0) return log_error_errno(r, "Failed to run event loop: %m"); - pty_forward_get_last_char(forward, &last_char); + if (forward) { + char last_char = 0; - forward = pty_forward_free(forward); + (void) pty_forward_get_last_char(forward, &last_char); + forward = pty_forward_free(forward); - if (!arg_quiet && last_char != '\n') - putc('\n', stdout); + if (!arg_quiet && last_char != '\n') + putc('\n', stdout); + } /* Kill if it is not dead yet anyway */ if (bus) { @@ -4171,7 +4618,7 @@ static int initialize_rlimits(void) { return 0; } -int main(int argc, char *argv[]) { +static int run(int argc, char *argv[]) { _cleanup_free_ char *console = NULL; _cleanup_close_ int master = -1; _cleanup_fdset_free_ FDSet *fds = NULL; @@ -4181,7 +4628,7 @@ int main(int argc, char *argv[]) { pid_t pid = 0; union in_addr_union exposed = {}; _cleanup_(release_lock_file) LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT; - bool interactive, veth_created = false, remove_tmprootdir = false; + bool veth_created = false, remove_tmprootdir = false; char tmprootdir[] = "/tmp/nspawn-root-XXXXXX"; _cleanup_(loop_device_unrefp) LoopDevice *loop = NULL; _cleanup_(decrypted_image_unrefp) DecryptedImage *decrypted_image = NULL; @@ -4190,10 +4637,6 @@ int main(int argc, char *argv[]) { log_parse_environment(); log_open(); - /* Make sure rename_process() in the stub init process can work */ - saved_argv = argv; - saved_argc = argc; - r = parse_argv(argc, argv); if (r <= 0) goto finish; @@ -4206,6 +4649,10 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; + r = load_oci_bundle(); + if (r < 0) + goto finish; + r = determine_names(); if (r < 0) goto finish; @@ -4214,8 +4661,6 @@ 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"); @@ -4398,7 +4843,7 @@ int main(int argc, char *argv[]) { goto finish; } - r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, COPY_REFLINK); + r = copy_file(arg_image, np, O_EXCL, arg_read_only ? 0400 : 0600, FS_NOCOW_FL, FS_NOCOW_FL, COPY_REFLINK|COPY_CRTIME); if (r < 0) { r = log_error_errno(r, "Failed to copy image file: %m"); goto finish; @@ -4481,31 +4926,37 @@ int main(int argc, char *argv[]) { if (r < 0) goto finish; - interactive = - isatty(STDIN_FILENO) > 0 && - isatty(STDOUT_FILENO) > 0; + if (arg_console_mode < 0) + arg_console_mode = + isatty(STDIN_FILENO) > 0 && + isatty(STDOUT_FILENO) > 0 ? CONSOLE_INTERACTIVE : CONSOLE_READ_ONLY; - master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); - if (master < 0) { - r = log_error_errno(errno, "Failed to acquire pseudo tty: %m"); - goto finish; - } + if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */ + arg_quiet = true; - r = ptsname_malloc(master, &console); - if (r < 0) { - r = log_error_errno(r, "Failed to determine tty name: %m"); - goto finish; - } - - if (arg_selinux_apifs_context) { - r = mac_selinux_apply(console, arg_selinux_apifs_context); - if (r < 0) + if (arg_console_mode != CONSOLE_PIPE) { + master = posix_openpt(O_RDWR|O_NOCTTY|O_CLOEXEC|O_NONBLOCK); + if (master < 0) { + r = log_error_errno(errno, "Failed to acquire pseudo tty: %m"); goto finish; - } + } - if (unlockpt(master) < 0) { - r = log_error_errno(errno, "Failed to unlock tty: %m"); - goto finish; + r = ptsname_malloc(master, &console); + if (r < 0) { + r = log_error_errno(r, "Failed to determine tty name: %m"); + goto finish; + } + + if (arg_selinux_apifs_context) { + r = mac_selinux_apply(console, arg_selinux_apifs_context); + if (r < 0) + goto finish; + } + + if (unlockpt(master) < 0) { + r = log_error_errno(errno, "Failed to unlock tty: %m"); + goto finish; + } } if (!arg_quiet) @@ -4520,22 +4971,22 @@ int main(int argc, char *argv[]) { } for (;;) { - r = run(master, - console, - dissected_image, - interactive, secondary, - fds, - veth_name, &veth_created, - &exposed, - &pid, &ret); + r = run_container(master, + console, + dissected_image, + secondary, + fds, + veth_name, &veth_created, + &exposed, + &pid, &ret); if (r <= 0) break; } finish: - sd_notify(false, - r == 0 && ret == EXIT_FORCE_RESTART ? "STOPPING=1\nSTATUS=Restarting..." : - "STOPPING=1\nSTATUS=Terminating..."); + (void) sd_notify(false, + r == 0 && ret == EXIT_FORCE_RESTART ? "STOPPING=1\nSTATUS=Restarting..." : + "STOPPING=1\nSTATUS=Terminating..."); if (pid > 0) (void) kill(pid, SIGKILL); @@ -4582,32 +5033,15 @@ finish: (void) remove_veth_links(veth_name, arg_network_veth_extra); (void) remove_bridge(arg_network_zone); - free(arg_directory); - free(arg_template); - free(arg_image); - free(arg_machine); - free(arg_hostname); - free(arg_user); - free(arg_pivot_root_new); - free(arg_pivot_root_old); - free(arg_chdir); - strv_free(arg_setenv); - free(arg_network_bridge); - strv_free(arg_network_interfaces); - strv_free(arg_network_macvlan); - strv_free(arg_network_ipvlan); - strv_free(arg_network_veth_extra); - strv_free(arg_parameters); - free(arg_network_zone); - free(arg_network_namespace_path); - strv_free(arg_property); custom_mount_free_all(arg_custom_mounts, arg_n_custom_mounts); expose_port_free_all(arg_expose_ports); - free(arg_root_hash); rlimit_free_all(arg_rlimit); - strv_free(arg_syscall_whitelist); - strv_free(arg_syscall_blacklist); - arg_cpuset = cpu_set_mfree(arg_cpuset); + device_node_array_free(arg_extra_nodes, arg_n_extra_nodes); - return r < 0 ? EXIT_FAILURE : ret; + if (r < 0) + return r; + + return ret; } + +DEFINE_MAIN_FUNCTION_WITH_POSITIVE_FAILURE(run); diff --git a/src/nss-myhostname/nss-myhostname.c b/src/nss-myhostname/nss-myhostname.c index e491351de..90de140a9 100644 --- a/src/nss-myhostname/nss-myhostname.c +++ b/src/nss-myhostname/nss-myhostname.c @@ -8,13 +8,13 @@ #include #include "alloc-util.h" +#include "errno-util.h" #include "hostname-util.h" #include "local-addresses.h" #include "macro.h" #include "nss-util.h" #include "signal-util.h" #include "string-util.h" -#include "util.h" /* We use 127.0.0.2 as IPv4 address. This has the advantage over * 127.0.0.1 that it can be translated back to the local hostname. For @@ -64,10 +64,8 @@ enum nss_status _nss_myhostname_gethostbyname4_r( } else if (is_gateway_hostname(name)) { n_addresses = local_gateways(NULL, 0, AF_UNSPEC, &addresses); - if (n_addresses <= 0) { - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } + if (n_addresses <= 0) + goto not_found; canonical = "_gateway"; @@ -81,10 +79,8 @@ 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), ".")) { - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } + if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) + goto not_found; n_addresses = local_addresses(NULL, 0, AF_UNSPEC, &addresses); if (n_addresses < 0) @@ -164,6 +160,10 @@ enum nss_status _nss_myhostname_gethostbyname4_r( h_errno = 0; return NSS_STATUS_SUCCESS; + +not_found: + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; } static enum nss_status fill_in_hostent( @@ -339,10 +339,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r( } else if (is_gateway_hostname(name)) { n_addresses = local_gateways(NULL, 0, af, &addresses); - if (n_addresses <= 0) { - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } + if (n_addresses <= 0) + goto not_found; canonical = "_gateway"; @@ -355,10 +353,8 @@ enum nss_status _nss_myhostname_gethostbyname3_r( return NSS_STATUS_TRYAGAIN; } - if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) { - *h_errnop = HOST_NOT_FOUND; - return NSS_STATUS_NOTFOUND; - } + if (!streq(name, hn) && !streq_ptr(startswith(name, hn), ".")) + goto not_found; n_addresses = local_addresses(NULL, 0, af, &addresses); if (n_addresses < 0) @@ -381,6 +377,10 @@ enum nss_status _nss_myhostname_gethostbyname3_r( errnop, h_errnop, ttlp, canonp); + +not_found: + *h_errnop = HOST_NOT_FOUND; + return NSS_STATUS_NOTFOUND; } enum nss_status _nss_myhostname_gethostbyaddr2_r( diff --git a/src/nss-mymachines/nss-mymachines.c b/src/nss-mymachines/nss-mymachines.c index 486a65895..0e76c4317 100644 --- a/src/nss-mymachines/nss-mymachines.c +++ b/src/nss-mymachines/nss-mymachines.c @@ -9,14 +9,16 @@ #include "alloc-util.h" #include "bus-common-errors.h" #include "env-util.h" +#include "errno-util.h" +#include "format-util.h" #include "hostname-util.h" #include "in-addr-util.h" #include "macro.h" +#include "memory-util.h" #include "nss-util.h" #include "signal-util.h" #include "string-util.h" #include "user-util.h" -#include "util.h" NSS_GETHOSTBYNAME_PROTOTYPES(mymachines); NSS_GETPW_PROTOTYPES(mymachines); @@ -230,7 +232,7 @@ enum nss_status _nss_mymachines_gethostbyname4_r( fail: UNPROTECT_ERRNO; *errnop = -r; - *h_errnop = NO_DATA; + *h_errnop = NO_RECOVERY; return NSS_STATUS_UNAVAIL; } @@ -401,7 +403,7 @@ enum nss_status _nss_mymachines_gethostbyname3_r( fail: UNPROTECT_ERRNO; *errnop = -r; - *h_errnop = NO_DATA; + *h_errnop = NO_RECOVERY; return NSS_STATUS_UNAVAIL; } diff --git a/src/nss-resolve/nss-resolve.c b/src/nss-resolve/nss-resolve.c index 8370fed07..ac5fff523 100644 --- a/src/nss-resolve/nss-resolve.c +++ b/src/nss-resolve/nss-resolve.c @@ -5,17 +5,19 @@ #include #include #include +#include +#include #include "sd-bus.h" #include "bus-common-errors.h" +#include "errno-util.h" #include "in-addr-util.h" #include "macro.h" #include "nss-util.h" #include "resolved-def.h" -#include "string-util.h" -#include "util.h" #include "signal-util.h" +#include "string-util.h" NSS_GETHOSTBYNAME_PROTOTYPES(resolve); NSS_GETHOSTBYADDR_PROTOTYPES(resolve); @@ -24,7 +26,9 @@ static bool bus_error_shall_fallback(sd_bus_error *e) { return sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN) || sd_bus_error_has_name(e, SD_BUS_ERROR_NAME_HAS_NO_OWNER) || sd_bus_error_has_name(e, SD_BUS_ERROR_NO_REPLY) || - sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED); + sd_bus_error_has_name(e, SD_BUS_ERROR_ACCESS_DENIED) || + sd_bus_error_has_name(e, SD_BUS_ERROR_DISCONNECTED) || + sd_bus_error_has_name(e, SD_BUS_ERROR_TIMEOUT); } static int count_addresses(sd_bus_message *m, int af, const char **canonical) { @@ -116,7 +120,6 @@ enum nss_status _nss_resolve_gethostbyname4_r( _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - enum nss_status ret = NSS_STATUS_UNAVAIL; const char *canonical = NULL; size_t l, ms, idx; char *r_name; @@ -160,8 +163,7 @@ 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") || - !bus_error_shall_fallback(&error)) + if (!bus_error_shall_fallback(&error)) goto not_found; /* Return NSS_STATUS_UNAVAIL when communication with systemd-resolved fails, @@ -271,7 +273,7 @@ fail: UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_RECOVERY; - return ret; + return NSS_STATUS_UNAVAIL; not_found: *h_errnop = HOST_NOT_FOUND; @@ -291,7 +293,6 @@ enum nss_status _nss_resolve_gethostbyname3_r( _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char *r_name, *r_aliases, *r_addr, *r_addr_list; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - enum nss_status ret = NSS_STATUS_UNAVAIL; size_t l, idx, ms, alen; const char *canonical; int c, r, i = 0; @@ -342,8 +343,7 @@ 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") || - !bus_error_shall_fallback(&error)) + if (!bus_error_shall_fallback(&error)) goto not_found; goto fail; @@ -461,7 +461,7 @@ fail: UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_RECOVERY; - return ret; + return NSS_STATUS_UNAVAIL; not_found: *h_errnop = HOST_NOT_FOUND; @@ -480,7 +480,6 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; char *r_name, *r_aliases, *r_addr, *r_addr_list; _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; - enum nss_status ret = NSS_STATUS_UNAVAIL; unsigned c = 0, i = 0; size_t ms = 0, idx; const char *n; @@ -503,10 +502,8 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( } if (len != FAMILY_ADDRESS_SIZE(af)) { - UNPROTECT_ERRNO; - *errnop = EINVAL; - *h_errnop = NO_RECOVERY; - return NSS_STATUS_UNAVAIL; + r = -EINVAL; + goto fail; } if (avoid_deadlock()) { @@ -546,8 +543,7 @@ 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") || - !bus_error_shall_fallback(&error)) + if (!bus_error_shall_fallback(&error)) goto not_found; goto fail; @@ -572,7 +568,7 @@ enum nss_status _nss_resolve_gethostbyaddr2_r( r = sd_bus_message_rewind(reply, false); if (r < 0) - return r; + goto fail; if (c <= 0) goto not_found; @@ -646,7 +642,7 @@ fail: UNPROTECT_ERRNO; *errnop = -r; *h_errnop = NO_RECOVERY; - return ret; + return NSS_STATUS_UNAVAIL; not_found: *h_errnop = HOST_NOT_FOUND; diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index f8db27ae2..8beae06d8 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -10,6 +10,7 @@ #include "dirent-util.h" #include "env-util.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "list.h" #include "macro.h" diff --git a/src/partition/growfs.c b/src/partition/growfs.c index c9d1c2779..60a310946 100644 --- a/src/partition/growfs.c +++ b/src/partition/growfs.c @@ -25,6 +25,7 @@ #include "pretty-print.h" #include "stat-util.h" #include "strv.h" +#include "util.h" static const char *arg_target = NULL; static bool arg_dry_run = false; diff --git a/src/portable/org.freedesktop.portable1.conf b/src/portable/org.freedesktop.portable1.conf index 4c20a886e..31d41af12 100644 --- a/src/portable/org.freedesktop.portable1.conf +++ b/src/portable/org.freedesktop.portable1.conf @@ -47,7 +47,7 @@ + send_member="GetImageMetadata"/> + send_member="GetMetadata"/> + send_member="GetState"/> +#include +#include +#include + #include "alloc-util.h" #include "bus-common-errors.h" #include "bus-label.h" @@ -523,7 +528,7 @@ const sd_bus_vtable image_vtable[] = { SD_BUS_PROPERTY("UsageExclusive", "t", NULL, offsetof(Image, usage_exclusive), 0), SD_BUS_PROPERTY("LimitExclusive", "t", NULL, offsetof(Image, limit_exclusive), 0), SD_BUS_METHOD("GetOSRelease", NULL, "a{ss}", bus_image_method_get_os_release, SD_BUS_VTABLE_UNPRIVILEGED), - SD_BUS_METHOD("GetMedatadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED), + SD_BUS_METHOD("GetMetadata", "as", "saya{say}", bus_image_method_get_metadata, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("GetState", NULL, "s", bus_image_method_get_state, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Attach", "assbs", "a(sss)", bus_image_method_attach, SD_BUS_VTABLE_UNPRIVILEGED), SD_BUS_METHOD("Detach", "b", "a(sss)", bus_image_method_detach, SD_BUS_VTABLE_UNPRIVILEGED), diff --git a/src/portable/portabled.c b/src/portable/portabled.c index 49a359fd3..5464360f8 100644 --- a/src/portable/portabled.c +++ b/src/portable/portabled.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "sd-bus.h" #include "sd-daemon.h" diff --git a/src/quotacheck/quotacheck.c b/src/quotacheck/quotacheck.c index 90f542a05..4e83fc83d 100644 --- a/src/quotacheck/quotacheck.c +++ b/src/quotacheck/quotacheck.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include "main-func.h" diff --git a/src/random-seed/random-seed.c b/src/random-seed/random-seed.c index 0c5f32975..71c2dba43 100644 --- a/src/random-seed/random-seed.c +++ b/src/random-seed/random-seed.c @@ -51,7 +51,7 @@ static int run(int argc, char *argv[]) { if (buf_size < POOL_SIZE_MIN) buf_size = POOL_SIZE_MIN; - r = mkdir_parents_label(RANDOM_SEED, 0755); + r = mkdir_parents(RANDOM_SEED, 0755); if (r < 0) return log_error_errno(r, "Failed to create directory " RANDOM_SEED_DIR ": %m"); diff --git a/src/remount-fs/remount-fs.c b/src/remount-fs/remount-fs.c index 0bac355e0..0df29aa69 100644 --- a/src/remount-fs/remount-fs.c +++ b/src/remount-fs/remount-fs.c @@ -47,6 +47,31 @@ static int track_pid(Hashmap **h, const char *path, pid_t pid) { return 0; } +static int do_remount(const char *path, bool force_rw, Hashmap **pids) { + pid_t pid; + int r; + + log_debug("Remounting %s...", path); + + r = safe_fork(force_rw ? "(remount-rw)" : "(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, + path, + "-o", + force_rw ? "remount,rw" : "remount")); + log_error_errno(errno, "Failed to execute " MOUNT_PATH ": %m"); + _exit(EXIT_FAILURE); + } + + /* Parent */ + return track_pid(pids, path, pid); +} + static int run(int argc, char *argv[]) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_endmntent_ FILE *f = NULL; @@ -66,36 +91,20 @@ static int run(int argc, char *argv[]) { if (!f) { if (errno != ENOENT) return log_error_errno(errno, "Failed to open /etc/fstab: %m"); - } else { + } else while ((me = getmntent(f))) { - pid_t pid; - - /* Remount the root fs, /usr and all API VFS */ + /* Remount the root fs, /usr, and all API VFSs */ if (!mount_point_is_api(me->mnt_dir) && !PATH_IN_SET(me->mnt_dir, "/", "/usr")) continue; - log_debug("Remounting %s...", me->mnt_dir); - if (path_equal(me->mnt_dir, "/")) has_root = true; - 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); - } - - /* Parent */ - r = track_pid(&pids, me->mnt_dir, pid); + r = do_remount(me->mnt_dir, false, &pids); if (r < 0) return r; } - } if (!has_root) { /* The $SYSTEMD_REMOUNT_ROOT_RW environment variable is set by systemd-gpt-auto-generator to tell us @@ -103,27 +112,14 @@ static int run(int argc, char *argv[]) { * 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); - } - - r = track_pid(&pids, "/", pid); - if (r < 0) - return r; - - } else if (r < 0 && r != -ENXIO) + if (r < 0 && r != -ENXIO) log_warning_errno(r, "Failed to parse $SYSTEMD_REMOUNT_ROOT_RW, ignoring: %m"); + + if (r > 0) { + r = do_remount("/", true, &pids); + if (r < 0) + return r; + } } r = 0; diff --git a/src/reply-password/reply-password.c b/src/reply-password/reply-password.c index ee7a0ea13..f8f6c2d3e 100644 --- a/src/reply-password/reply-password.c +++ b/src/reply-password/reply-password.c @@ -11,6 +11,7 @@ #include "fileio.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "socket-util.h" #include "string-util.h" #include "util.h" diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c index 4d533f851..1d2fc7130 100644 --- a/src/resolve/resolvectl.c +++ b/src/resolve/resolvectl.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include "sd-bus.h" @@ -118,10 +119,8 @@ int ifname_mangle(const char *s) { } } - if (arg_ifindex > 0 && arg_ifindex != ifi) { - log_error("Specified multiple different interfaces. Refusing."); - return -EINVAL; - } + if (arg_ifindex > 0 && arg_ifindex != ifi) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified multiple different interfaces. Refusing."); arg_ifindex = ifi; free_and_replace(arg_ifname, iface); diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c index 5b547ba7e..4fc281e80 100644 --- a/src/resolve/resolved-bus.c +++ b/src/resolve/resolved-bus.c @@ -4,12 +4,13 @@ #include "bus-common-errors.h" #include "bus-util.h" #include "dns-domain.h" +#include "memory-util.h" #include "missing_capability.h" #include "resolved-bus.h" #include "resolved-def.h" #include "resolved-dns-synthesize.h" -#include "resolved-dnssd.h" #include "resolved-dnssd-bus.h" +#include "resolved-dnssd.h" #include "resolved-link-bus.h" #include "user-util.h" #include "utf8.h" diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c index d7252d3da..0ee7340ff 100644 --- a/src/resolve/resolved-dns-answer.c +++ b/src/resolve/resolved-dns-answer.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "dns-domain.h" #include "resolved-dns-answer.h" @@ -87,40 +89,33 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl if (a->items[i].ifindex != ifindex) continue; - r = dns_resource_record_equal(a->items[i].rr, rr); - if (r < 0) - return r; - if (r > 0) { - /* Don't mix contradicting TTLs (see below) */ - if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) - return -EINVAL; - - /* Entry already exists, keep the entry with - * the higher RR. */ - if (rr->ttl > a->items[i].rr->ttl) { - dns_resource_record_ref(rr); - dns_resource_record_unref(a->items[i].rr); - a->items[i].rr = rr; - } - - a->items[i].flags |= flags; - return 0; - } - r = dns_resource_key_equal(a->items[i].rr->key, rr->key); if (r < 0) return r; - if (r > 0) { - /* There's already an RR of the same RRset in - * place! Let's see if the TTLs more or less - * match. We don't really care if they match - * precisely, but we do care whether one is 0 - * and the other is not. See RFC 2181, Section - * 5.2. */ + if (r == 0) + continue; - if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) - return -EINVAL; + /* There's already an RR of the same RRset in place! Let's see if the TTLs more or less + * match. We don't really care if they match precisely, but we do care whether one is 0 and + * the other is not. See RFC 2181, Section 5.2. */ + if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0)) + return -EINVAL; + + r = dns_resource_record_payload_equal(a->items[i].rr, rr); + if (r < 0) + return r; + if (r == 0) + continue; + + /* Entry already exists, keep the entry with the higher RR. */ + if (rr->ttl > a->items[i].rr->ttl) { + dns_resource_record_ref(rr); + dns_resource_record_unref(a->items[i].rr); + a->items[i].rr = rr; } + + a->items[i].flags |= flags; + return 0; } return dns_answer_add_raw(a, rr, ifindex, flags); diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c index 14acc4e77..a5ded5ada 100644 --- a/src/resolve/resolved-dns-dnssec.c +++ b/src/resolve/resolved-dns-dnssec.c @@ -12,8 +12,10 @@ #include "fileio.h" #include "gcrypt-util.h" #include "hexdecoct.h" +#include "memory-util.h" #include "resolved-dns-dnssec.h" #include "resolved-dns-packet.h" +#include "sort-util.h" #include "string-table.h" #define VERIFY_RRS_MAX 256 @@ -1797,22 +1799,14 @@ static int dnssec_nsec_covers(DnsResourceRecord *rr, const char *name) { return dns_name_between(dns_resource_key_name(rr->key), name, rr->nsec.next_domain_name); } -static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) { - _cleanup_free_ char *wc = NULL; - const char *common_suffix, *signer; - int r; +static int dnssec_nsec_generate_wildcard(DnsResourceRecord *rr, const char *name, char **wc) { + const char *common_suffix1, *common_suffix2, *signer; + int r, labels1, labels2; assert(rr); assert(rr->key->type == DNS_TYPE_NSEC); - /* Checks whether the "Wildcard at the Closest Encloser" is within the space covered by the specified - * RR. Specifically, checks whether 'name' has the common suffix of the NSEC RR's owner and next names as - * suffix, and whether the NSEC covers the name generated by that suffix prepended with an asterisk label. - * - * NSEC bar → waldo.foo.bar: indicates that *.bar and *.foo.bar do not exist - * NSEC waldo.foo.bar → yyy.zzz.xoo.bar: indicates that *.xoo.bar and *.zzz.xoo.bar do not exist (and more ...) - * NSEC yyy.zzz.xoo.bar → bar: indicates that a number of wildcards don#t exist either... - */ + /* Generates "Wildcard at the Closest Encloser" for the given name and NSEC RR. */ r = dns_resource_record_signer(rr, &signer); if (r < 0) @@ -1822,23 +1816,31 @@ static int dnssec_nsec_covers_wildcard(DnsResourceRecord *rr, const char *name) if (r <= 0) return r; - r = dns_name_endswith(name, dns_resource_key_name(rr->key)); - if (r < 0) - 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), 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, 0, &wc); - } + r = dns_name_common_suffix(name, dns_resource_key_name(rr->key), &common_suffix1); if (r < 0) return r; - return dns_name_between(dns_resource_key_name(rr->key), wc, rr->nsec.next_domain_name); + r = dns_name_common_suffix(name, rr->nsec.next_domain_name, &common_suffix2); + if (r < 0) + return r; + + labels1 = dns_name_count_labels(common_suffix1); + if (labels1 < 0) + return labels1; + + labels2 = dns_name_count_labels(common_suffix2); + if (labels2 < 0) + return labels2; + + if (labels1 > labels2) + r = dns_name_concat("*", common_suffix1, 0, wc); + else + r = dns_name_concat("*", common_suffix2, 0, wc); + + if (r < 0) + return r; + + return 0; } int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *result, bool *authenticated, uint32_t *ttl) { @@ -1942,14 +1944,30 @@ int dnssec_nsec_test(DnsAnswer *answer, DnsResourceKey *key, DnssecNsecResult *r covering_rr = rr; covering_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED; } + } - /* Check if this NSEC RR proves the absence of a wildcard RR under this name */ - r = dnssec_nsec_covers_wildcard(rr, name); + if (covering_rr) { + _cleanup_free_ char *wc = NULL; + r = dnssec_nsec_generate_wildcard(covering_rr, name, &wc); if (r < 0) return r; - if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) { - wildcard_rr = rr; - wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED; + + DNS_ANSWER_FOREACH_FLAGS(rr, flags, answer) { + + if (rr->key->class != key->class) + continue; + + if (rr->key->type != DNS_TYPE_NSEC) + continue; + + /* Check if this NSEC RR proves the nonexistence of the wildcard */ + r = dnssec_nsec_covers(rr, wc); + if (r < 0) + return r; + if (r > 0 && (!wildcard_rr || !wildcard_rr_authenticated)) { + wildcard_rr = rr; + wildcard_rr_authenticated = flags & DNS_ANSWER_AUTHENTICATED; + } } } diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c index 572271be9..a4959cd7c 100644 --- a/src/resolve/resolved-dns-packet.c +++ b/src/resolve/resolved-dns-packet.c @@ -6,7 +6,9 @@ #include "alloc-util.h" #include "dns-domain.h" +#include "memory-util.h" #include "resolved-dns-packet.h" +#include "set.h" #include "string-table.h" #include "strv.h" #include "unaligned.h" @@ -2133,6 +2135,17 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) if (!question) return -ENOMEM; + _cleanup_set_free_ Set *keys = NULL; /* references to keys are kept by Question */ + + keys = set_new(&dns_resource_key_hash_ops); + if (!keys) + return log_oom(); + + r = set_reserve(keys, n * 2); /* Higher multipliers give slightly higher efficiency through + * hash collisions, but the gains quickly drop of after 2. */ + if (r < 0) + return r; + for (i = 0; i < n; i++) { _cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL; bool cache_flush; @@ -2147,7 +2160,14 @@ static int dns_packet_extract_question(DnsPacket *p, DnsQuestion **ret_question) if (!dns_type_is_valid_query(key->type)) return -EBADMSG; - r = dns_question_add(question, key); + r = set_put(keys, key); + if (r < 0) + return r; + if (r == 0) + /* Already in the Question, let's skip */ + continue; + + r = dns_question_add_raw(question, key); if (r < 0) return r; } @@ -2239,7 +2259,7 @@ static int dns_packet_extract_answer(DnsPacket *p, DnsAnswer **ret_answer) { * be contained in questions, never in replies. Crappy * Belkin routers copy the OPT data for example, hence let's * detect this so that we downgrade early. */ - log_debug("OPT RR contained RFC6975 data, ignoring."); + log_debug("OPT RR contains RFC6975 data, ignoring."); bad_opt = true; continue; } diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h index 008860b5d..56614c4a0 100644 --- a/src/resolve/resolved-dns-packet.h +++ b/src/resolve/resolved-dns-packet.h @@ -119,12 +119,12 @@ static inline uint16_t DNS_PACKET_PAYLOAD_SIZE_MAX(DnsPacket *p) { /* Returns the advertised maximum size for replies, or the DNS default if there's nothing defined. */ + if (p->ipproto == IPPROTO_TCP) /* we ignore EDNS(0) size data on TCP, like everybody else */ + return DNS_PACKET_SIZE_MAX; + 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 535ef4e77..4a41921cf 100644 --- a/src/resolve/resolved-dns-query.c +++ b/src/resolve/resolved-dns-query.c @@ -387,10 +387,8 @@ DnsQuery *dns_query_free(DnsQuery *q) { if (q->request_dns_stream) { /* Detach the stream from our query, in case something else keeps a reference to it. */ - q->request_dns_stream->complete = NULL; - q->request_dns_stream->on_packet = NULL; - q->request_dns_stream->query = NULL; - dns_stream_unref(q->request_dns_stream); + (void) set_remove(q->request_dns_stream->queries, q); + q->request_dns_stream = dns_stream_unref(q->request_dns_stream); } free(q->request_address_string); diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c index 1ed917156..60cd34bcf 100644 --- a/src/resolve/resolved-dns-question.c +++ b/src/resolve/resolved-dns-question.c @@ -32,8 +32,20 @@ static DnsQuestion *dns_question_free(DnsQuestion *q) { DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsQuestion, dns_question, dns_question_free); +int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key) { + /* Insert without checking for duplicates. */ + + assert(key); + assert(q); + + if (q->n_keys >= q->n_allocated) + return -ENOSPC; + + q->keys[q->n_keys++] = dns_resource_key_ref(key); + return 0; +} + int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { - size_t i; int r; assert(key); @@ -41,7 +53,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { if (!q) return -ENOSPC; - for (i = 0; i < q->n_keys; i++) { + for (size_t i = 0; i < q->n_keys; i++) { r = dns_resource_key_equal(q->keys[i], key); if (r < 0) return r; @@ -49,11 +61,7 @@ int dns_question_add(DnsQuestion *q, DnsResourceKey *key) { return 0; } - if (q->n_keys >= q->n_allocated) - return -ENOSPC; - - q->keys[q->n_keys++] = dns_resource_key_ref(key); - return 0; + return dns_question_add_raw(q, key); } int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain) { diff --git a/src/resolve/resolved-dns-question.h b/src/resolve/resolved-dns-question.h index f513bf032..0803f49b8 100644 --- a/src/resolve/resolved-dns-question.h +++ b/src/resolve/resolved-dns-question.h @@ -22,6 +22,7 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo int dns_question_new_reverse(DnsQuestion **ret, int family, const union in_addr_union *a); int dns_question_new_service(DnsQuestion **ret, const char *service, const char *type, const char *domain, bool with_txt, bool convert_idna); +int dns_question_add_raw(DnsQuestion *q, DnsResourceKey *key); int dns_question_add(DnsQuestion *q, DnsResourceKey *key); int dns_question_matches_rr(DnsQuestion *q, DnsResourceRecord *rr, const char *search_domain); diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c index a1dffb08a..4cbb9723e 100644 --- a/src/resolve/resolved-dns-rr.c +++ b/src/resolve/resolved-dns-rr.c @@ -557,18 +557,10 @@ int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const u ((a).field ## _size == (b).field ## _size && \ memcmp((a).field, (b).field, (a).field ## _size) == 0) -int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { +int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { int r; - assert(a); - assert(b); - - if (a == b) - return 1; - - r = dns_resource_key_equal(a->key, b->key); - if (r <= 0) - return r; + /* Check if a and b are the same, but don't look at their keys */ if (a->unparseable != b->unparseable) return 0; @@ -692,6 +684,22 @@ int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecor } } +int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b) { + int r; + + assert(a); + assert(b); + + if (a == b) + return 1; + + r = dns_resource_key_equal(a->key, b->key); + if (r <= 0) + return r; + + return dns_resource_record_payload_equal(a, b); +} + static char* format_location(uint32_t latitude, uint32_t longitude, uint32_t altitude, uint8_t size, uint8_t horiz_pre, uint8_t vert_pre) { char *s; @@ -954,7 +962,6 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { case DNS_TYPE_DNSKEY: { _cleanup_free_ char *alg = NULL; char *ss; - int n; uint16_t key_tag; key_tag = dnssec_keytag(rr, true); @@ -963,16 +970,15 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { if (r < 0) return NULL; - r = asprintf(&s, "%s %u %u %s %n", + r = asprintf(&s, "%s %u %u %s", k, rr->dnskey.flags, rr->dnskey.protocol, - alg, - &n); + alg); if (r < 0) return NULL; - r = base64_append(&s, n, + r = base64_append(&s, r, rr->dnskey.key, rr->dnskey.key_size, 8, columns()); if (r < 0) @@ -998,7 +1004,6 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { _cleanup_free_ char *alg = NULL; char expiration[STRLEN("YYYYMMDDHHmmSS") + 1], inception[STRLEN("YYYYMMDDHHmmSS") + 1]; const char *type; - int n; type = dns_type_to_string(rr->rrsig.type_covered); @@ -1017,7 +1022,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { /* TYPE?? follows * http://tools.ietf.org/html/rfc3597#section-5 */ - r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s %n", + r = asprintf(&s, "%s %s%.*u %s %u %u %s %s %u %s", k, type ?: "TYPE", type ? 0 : 1, type ? 0u : (unsigned) rr->rrsig.type_covered, @@ -1027,12 +1032,11 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { expiration, inception, rr->rrsig.key_tag, - rr->rrsig.signer, - &n); + rr->rrsig.signer); if (r < 0) return NULL; - r = base64_append(&s, n, + r = base64_append(&s, r, rr->rrsig.signature, rr->rrsig.signature_size, 8, columns()); if (r < 0) @@ -1138,15 +1142,11 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) { } case DNS_TYPE_OPENPGPKEY: { - int n; - - r = asprintf(&s, "%s %n", - k, - &n); + r = asprintf(&s, "%s", k); if (r < 0) return NULL; - r = base64_append(&s, n, + r = base64_append(&s, r, rr->generic.data, rr->generic.data_size, 8, columns()); if (r < 0) diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h index e02fa2d53..291447f00 100644 --- a/src/resolve/resolved-dns-rr.h +++ b/src/resolve/resolved-dns-rr.h @@ -9,6 +9,7 @@ #include "in-addr-util.h" #include "list.h" #include "string-util.h" +#include "time-util.h" typedef struct DnsResourceKey DnsResourceKey; typedef struct DnsResourceRecord DnsResourceRecord; @@ -308,6 +309,8 @@ DnsResourceRecord* dns_resource_record_unref(DnsResourceRecord *rr); int dns_resource_record_new_reverse(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); int dns_resource_record_new_address(DnsResourceRecord **ret, int family, const union in_addr_union *address, const char *name); int dns_resource_record_equal(const DnsResourceRecord *a, const DnsResourceRecord *b); +int dns_resource_record_payload_equal(const DnsResourceRecord *a, const DnsResourceRecord *b); + const char* dns_resource_record_to_string(DnsResourceRecord *rr); DnsResourceRecord *dns_resource_record_copy(DnsResourceRecord *rr); DEFINE_TRIVIAL_CLEANUP_FUNC(DnsResourceRecord*, dns_resource_record_unref); diff --git a/src/resolve/resolved-dns-stream.c b/src/resolve/resolved-dns-stream.c index aee339a4c..cb7b186fc 100644 --- a/src/resolve/resolved-dns-stream.c +++ b/src/resolve/resolved-dns-stream.c @@ -11,6 +11,8 @@ #define DNS_STREAM_TIMEOUT_USEC (10 * USEC_PER_SEC) #define DNS_STREAMS_MAX 128 +#define DNS_QUERIES_PER_STREAM 32 + static void dns_stream_stop(DnsStream *s) { assert(s); @@ -36,12 +38,16 @@ static int dns_stream_update_io(DnsStream *s) { s->n_written = 0; f |= EPOLLOUT; } - if (!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) + + /* Let's read a packet if we haven't queued any yet. Except if we already hit a limit of parallel + * queries for this connection. */ + if ((!s->read_packet || s->n_read < sizeof(s->read_size) + s->read_packet->size) && + set_size(s->queries) < DNS_QUERIES_PER_STREAM) f |= EPOLLIN; #if ENABLE_DNS_OVER_TLS /* For handshake and clean closing purposes, TLS can override requested events */ - if (s->dnstls_events) + if (s->dnstls_events != 0) f = s->dnstls_events; #endif @@ -52,6 +58,10 @@ 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); + assert(error >= 0); + + /* Error is > 0 when the connection failed for some reason in the network stack. It's == 0 if we sent + * and receieved exactly one packet each (in the LLMNR client case). */ #if ENABLE_DNS_OVER_TLS if (s->encrypted) { @@ -281,6 +291,7 @@ 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) { _cleanup_(dns_stream_unrefp) DnsStream *s = dns_stream_ref(userdata); /* Protect stream while we process it */ + bool progressed = false; int r; assert(s); @@ -324,8 +335,10 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use if (ss < 0) { if (!IN_SET(-ss, EINTR, EAGAIN)) return dns_stream_complete(s, -ss); - } else + } else { + progressed = true; s->n_written += ss; + } /* Are we done? If so, disable the event source for EPOLLOUT */ if (s->n_written >= sizeof(s->write_size) + s->write_packet->size) { @@ -348,8 +361,10 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use return dns_stream_complete(s, -ss); } else if (ss == 0) return dns_stream_complete(s, ECONNRESET); - else + else { + progressed = true; s->n_read += ss; + } } if (s->n_read >= sizeof(s->read_size)) { @@ -420,10 +435,21 @@ static int on_stream_io(sd_event_source *es, int fd, uint32_t revents, void *use } } - if ((s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && + /* Call "complete" callback if finished reading and writing one packet, and there's nothing else left + * to write. */ + if (s->type == DNS_STREAM_LLMNR_SEND && + (s->write_packet && s->n_written >= sizeof(s->write_size) + s->write_packet->size) && + ordered_set_isempty(s->write_queue) && (s->read_packet && s->n_read >= sizeof(s->read_size) + s->read_packet->size)) return dns_stream_complete(s, 0); + /* If we did something, let's restart the timeout event source */ + if (progressed && s->timeout_event_source) { + r = sd_event_source_set_time(s->timeout_event_source, now(clock_boottime_or_monotonic()) + DNS_STREAM_TIMEOUT_USEC); + if (r < 0) + log_warning_errno(errno, "Couldn't restart TCP connection timeout, ignoring: %m"); + } + return 0; } @@ -437,7 +463,7 @@ static DnsStream *dns_stream_free(DnsStream *s) { if (s->manager) { LIST_REMOVE(streams, s->manager->dns_streams, s); - s->manager->n_dns_streams--; + s->manager->n_dns_streams[s->type]--; } #if ENABLE_DNS_OVER_TLS @@ -462,6 +488,7 @@ DEFINE_TRIVIAL_REF_UNREF_FUNC(DnsStream, dns_stream, dns_stream_free); int dns_stream_new( Manager *m, DnsStream **ret, + DnsStreamType type, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address) { @@ -471,9 +498,13 @@ int dns_stream_new( assert(m); assert(ret); + assert(type >= 0); + assert(type < _DNS_STREAM_TYPE_MAX); + assert(protocol >= 0); + assert(protocol < _DNS_PROTOCOL_MAX); assert(fd >= 0); - if (m->n_dns_streams > DNS_STREAMS_MAX) + if (m->n_dns_streams[type] > DNS_STREAMS_MAX) return -EBUSY; s = new(DnsStream, 1); @@ -508,7 +539,7 @@ int dns_stream_new( (void) sd_event_source_set_description(s->timeout_event_source, "dns-stream-timeout"); LIST_PREPEND(streams, m->dns_streams, s); - m->n_dns_streams++; + m->n_dns_streams[type]++; s->manager = m; s->fd = fd; diff --git a/src/resolve/resolved-dns-stream.h b/src/resolve/resolved-dns-stream.h index f18fc919e..780051b38 100644 --- a/src/resolve/resolved-dns-stream.h +++ b/src/resolve/resolved-dns-stream.h @@ -5,6 +5,15 @@ typedef struct DnsStream DnsStream; +typedef enum DnsStreamType { + DNS_STREAM_LOOKUP, /* Outgoing connection to a classic DNS server */ + DNS_STREAM_LLMNR_SEND, /* Outgoing LLMNR TCP lookup */ + DNS_STREAM_LLMNR_RECV, /* Incoming LLMNR TCP lookup */ + DNS_STREAM_STUB, /* Incoming DNS stub connection */ + _DNS_STREAM_TYPE_MAX, + _DNS_STREAM_TYPE_INVALID = -1, +} DnsStreamType; + #include "resolved-dns-packet.h" #include "resolved-dns-transaction.h" #include "resolved-manager.h" @@ -25,6 +34,7 @@ struct DnsStream { Manager *manager; unsigned n_ref; + DnsStreamType type; DnsProtocol protocol; int fd; @@ -58,7 +68,7 @@ struct DnsStream { 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 */ + Set *queries; /* when used by the DNS stub logic */ /* used when DNS-over-TLS is enabled */ bool encrypted:1; @@ -66,7 +76,7 @@ struct DnsStream { LIST_FIELDS(DnsStream, streams); }; -int dns_stream_new(Manager *m, DnsStream **s, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address); +int dns_stream_new(Manager *m, DnsStream **s, DnsStreamType type, DnsProtocol protocol, int fd, const union sockaddr_union *tfo_address); #if ENABLE_DNS_OVER_TLS int dns_stream_connect_tls(DnsStream *s, void *tls_session); #endif diff --git a/src/resolve/resolved-dns-stub.c b/src/resolve/resolved-dns-stub.c index a00716cd8..ce994a7ee 100644 --- a/src/resolve/resolved-dns-stub.c +++ b/src/resolve/resolved-dns-stub.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include "errno-util.h" #include "fd-util.h" #include "missing_network.h" #include "resolved-dns-stub.h" @@ -91,7 +92,14 @@ static int dns_stub_finish_reply_packet( assert(p); - if (!add_opt) { + if (add_opt) { + r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL); + if (r == -EMSGSIZE) /* Hit the size limit? then indicate truncation */ + tc = true; + else if (r < 0) + return r; + + } else { /* If the client can't to EDNS0, don't do DO either */ edns0_do = false; @@ -117,23 +125,9 @@ static int dns_stub_finish_reply_packet( 0 /* cd */, rcode)); - if (add_opt) { - r = dns_packet_append_opt(p, ADVERTISE_DATAGRAM_SIZE_MAX, edns0_do, rcode, NULL); - if (r < 0) - return r; - } - return 0; } -static void dns_stub_detach_stream(DnsStream *s) { - assert(s); - - s->complete = NULL; - s->on_packet = NULL; - s->query = NULL; -} - static int dns_stub_send(Manager *m, DnsStream *s, DnsPacket *p, DnsPacket *reply) { int r; @@ -197,17 +191,19 @@ static void dns_stub_query_complete(DnsQuery *q) { break; } - r = dns_query_process_cname(q); - if (r == -ELOOP) { - (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false); - break; + if (!truncated) { + r = dns_query_process_cname(q); + if (r == -ELOOP) { + (void) dns_stub_send_failure(q->manager, q->request_dns_stream, q->request_dns_packet, DNS_RCODE_SERVFAIL, false); + break; + } + if (r < 0) { + log_debug_errno(r, "Failed to process CNAME: %m"); + break; + } + if (r == DNS_QUERY_RESTARTED) + return; } - if (r < 0) { - log_debug_errno(r, "Failed to process CNAME: %m"); - break; - } - if (r == DNS_QUERY_RESTARTED) - return; r = dns_stub_finish_reply_packet( q->reply_dns_packet, @@ -257,27 +253,27 @@ static void dns_stub_query_complete(DnsQuery *q) { assert_not_reached("Impossible state"); } - /* If there's a packet to write set, let's leave the stream around */ - if (q->request_dns_stream && DNS_STREAM_QUEUED(q->request_dns_stream)) { - - /* Detach the stream from our query (make it an orphan), but do not drop the reference to it. The - * default completion action of the stream will drop the reference. */ - - dns_stub_detach_stream(q->request_dns_stream); - q->request_dns_stream = NULL; - } - dns_query_free(q); } static int dns_stub_stream_complete(DnsStream *s, int error) { assert(s); - log_debug_errno(error, "DNS TCP connection terminated, destroying query: %m"); + log_debug_errno(error, "DNS TCP connection terminated, destroying queries: %m"); - assert(s->query); - dns_query_free(s->query); + for (;;) { + DnsQuery *q; + q = set_first(s->queries); + if (!q) + break; + + dns_query_free(q); + } + + /* This drops the implicit ref we keep around since it was allocated, as incoming stub connections + * should be kept as long as the client wants to. */ + dns_stream_unref(s); return 0; } @@ -289,8 +285,6 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { assert(p); assert(p->protocol == DNS_PROTOCOL_DNS); - /* Takes ownership of the *s stream object */ - if (in_addr_is_localhost(p->family, &p->sender) <= 0 || in_addr_is_localhost(p->family, &p->destination) <= 0) { log_error("Got packet on unexpected IP range, refusing."); @@ -351,9 +345,19 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { q->complete = dns_stub_query_complete; if (s) { - s->on_packet = NULL; - s->complete = dns_stub_stream_complete; - s->query = q; + /* Remember which queries belong to this stream, so that we can cancel them when the stream + * is disconnected early */ + + r = set_ensure_allocated(&s->queries, &trivial_hash_ops); + if (r < 0) { + log_oom(); + goto fail; + } + + if (set_put(s->queries, q) < 0) { + log_oom(); + goto fail; + } } r = dns_query_go(q); @@ -367,9 +371,6 @@ static void dns_stub_process_query(Manager *m, DnsStream *s, DnsPacket *p) { return; fail: - if (s && DNS_STREAM_QUEUED(s)) - dns_stub_detach_stream(s); - dns_query_free(q); } @@ -421,8 +422,9 @@ static int manager_dns_stub_udp_fd(Manager *m) { 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) - return -errno; + r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX); + if (r < 0) + return r; if (bind(fd, &sa.sa, sizeof(sa.in)) < 0) return -errno; @@ -451,10 +453,6 @@ static int on_dns_stub_stream_packet(DnsStream *s) { } else log_debug("Invalid DNS stub TCP packet, ignoring."); - /* Drop the reference to the stream. Either a query was created and added its own reference to the stream now, - * or that didn't happen in which case we want to free the stream */ - dns_stream_unref(s); - return 0; } @@ -465,22 +463,22 @@ static int on_dns_stub_stream(sd_event_source *s, int fd, uint32_t revents, void cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (cfd < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + if (ERRNO_IS_ACCEPT_AGAIN(errno)) return 0; return -errno; } - r = dns_stream_new(m, &stream, DNS_PROTOCOL_DNS, cfd, NULL); + r = dns_stream_new(m, &stream, DNS_STREAM_STUB, DNS_PROTOCOL_DNS, cfd, NULL); if (r < 0) { safe_close(cfd); return r; } stream->on_packet = on_dns_stub_stream_packet; + stream->complete = dns_stub_stream_complete; - /* We let the reference to the stream dangling here, it will either be dropped by the default "complete" action - * of the stream, or by our packet callback, or when the manager is shut down. */ + /* We let the reference to the stream dangle here, it will be dropped later by the complete callback. */ return 0; } @@ -518,8 +516,9 @@ static int manager_dns_stub_tcp_fd(Manager *m) { 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) - return -errno; + r = socket_bind_to_ifindex(fd, LOOPBACK_IFINDEX); + if (r < 0) + return r; if (bind(fd, &sa.sa, sizeof(sa.in)) < 0) return -errno; diff --git a/src/resolve/resolved-dns-transaction.c b/src/resolve/resolved-dns-transaction.c index 4a2d2ccbd..9cac91f53 100644 --- a/src/resolve/resolved-dns-transaction.c +++ b/src/resolve/resolved-dns-transaction.c @@ -6,6 +6,7 @@ #include "alloc-util.h" #include "dns-domain.h" #include "errno-list.h" +#include "errno-util.h" #include "fd-util.h" #include "random-util.h" #include "resolved-dns-cache.h" @@ -540,12 +541,8 @@ static int on_stream_packet(DnsStream *s) { if (t) return dns_transaction_on_stream_packet(t, p); - /* Ignore incorrect transaction id as transaction can have been canceled */ - if (dns_packet_validate_reply(p) <= 0) { - log_debug("Invalid TCP reply packet."); - on_stream_complete(s, 0); - } - + /* Ignore incorrect transaction id as an old transaction can have been canceled. */ + log_debug("Received unexpected TCP reply packet with id %" PRIu16 ", ignoring.", DNS_PACKET_ID(p)); return 0; } @@ -554,9 +551,10 @@ static uint16_t dns_port_for_feature_level(DnsServerFeatureLevel level) { } static int dns_transaction_emit_tcp(DnsTransaction *t) { - _cleanup_close_ int fd = -1; _cleanup_(dns_stream_unrefp) DnsStream *s = NULL; + _cleanup_close_ int fd = -1; union sockaddr_union sa; + DnsStreamType type; int r; assert(t); @@ -582,6 +580,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { else fd = dns_scope_socket_tcp(t->scope, AF_UNSPEC, NULL, t->server, dns_port_for_feature_level(t->current_feature_level), &sa); + type = DNS_STREAM_LOOKUP; break; case DNS_PROTOCOL_LLMNR: @@ -607,6 +606,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { fd = dns_scope_socket_tcp(t->scope, family, &address, NULL, LLMNR_PORT, &sa); } + type = DNS_STREAM_LLMNR_SEND; break; default: @@ -617,7 +617,7 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { if (fd < 0) return fd; - r = dns_stream_new(t->scope->manager, &s, t->scope->protocol, fd, &sa); + r = dns_stream_new(t->scope->manager, &s, type, t->scope->protocol, fd, &sa); if (r < 0) return r; @@ -636,8 +636,8 @@ static int dns_transaction_emit_tcp(DnsTransaction *t) { if (t->server) { dns_server_unref_stream(t->server); - t->server->stream = dns_stream_ref(s); s->server = dns_server_ref(t->server); + t->server->stream = dns_stream_ref(s); } s->complete = on_stream_complete; diff --git a/src/resolve/resolved-dns-trust-anchor.c b/src/resolve/resolved-dns-trust-anchor.c index c5ec93b72..d25b5f9d9 100644 --- a/src/resolve/resolved-dns-trust-anchor.c +++ b/src/resolve/resolved-dns-trust-anchor.c @@ -9,10 +9,12 @@ #include "fd-util.h" #include "fileio.h" #include "hexdecoct.h" +#include "nulstr-util.h" #include "parse-util.h" -#include "resolved-dns-trust-anchor.h" #include "resolved-dns-dnssec.h" +#include "resolved-dns-trust-anchor.h" #include "set.h" +#include "sort-util.h" #include "string-util.h" #include "strv.h" diff --git a/src/resolve/resolved-dnssd.c b/src/resolve/resolved-dnssd.c index b8e6a7abe..0e6fa1d0c 100644 --- a/src/resolve/resolved-dnssd.c +++ b/src/resolve/resolved-dnssd.c @@ -1,21 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ #include "conf-files.h" #include "conf-parser.h" +#include "def.h" #include "resolved-dnssd.h" #include "resolved-dns-rr.h" #include "resolved-manager.h" #include "specifier.h" #include "strv.h" -const char* const dnssd_service_dirs[] = { - "/etc/systemd/dnssd", - "/run/systemd/dnssd", - "/usr/lib/systemd/dnssd", -#if HAVE_SPLIT_USR - "/lib/systemd/dnssd", -#endif - NULL -}; +#define DNSSD_SERVICE_DIRS ((const char* const*) CONF_PATHS_STRV("systemd/dnssd")) DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data) { if (!txt_data) @@ -92,7 +86,7 @@ static int dnssd_service_load(Manager *manager, const char *filename) { dropin_dirname = strjoina(service->name, ".dnssd.d"); - r = config_parse_many(filename, dnssd_service_dirs, dropin_dirname, + r = config_parse_many(filename, DNSSD_SERVICE_DIRS, dropin_dirname, "Service\0", config_item_perf_lookup, resolved_dnssd_gperf_lookup, false, service); @@ -195,7 +189,7 @@ int dnssd_load(Manager *manager) { if (manager->mdns_support != RESOLVE_SUPPORT_YES) return 0; - r = conf_files_list_strv(&files, ".dnssd", NULL, 0, dnssd_service_dirs); + r = conf_files_list_strv(&files, ".dnssd", NULL, 0, DNSSD_SERVICE_DIRS); if (r < 0) return log_error_errno(r, "Failed to enumerate .dnssd files: %m"); diff --git a/src/resolve/resolved-etc-hosts.c b/src/resolve/resolved-etc-hosts.c index ee21222e5..6a23d2bb8 100644 --- a/src/resolve/resolved-etc-hosts.c +++ b/src/resolve/resolved-etc-hosts.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "fd-util.h" #include "fileio.h" #include "hostname-util.h" diff --git a/src/resolve/resolved-llmnr.c b/src/resolve/resolved-llmnr.c index dfa55c577..61e50343e 100644 --- a/src/resolve/resolved-llmnr.c +++ b/src/resolve/resolved-llmnr.c @@ -3,6 +3,7 @@ #include #include +#include "errno-util.h" #include "fd-util.h" #include "resolved-llmnr.h" #include "resolved-manager.h" @@ -289,19 +290,21 @@ static int on_llmnr_stream(sd_event_source *s, int fd, uint32_t revents, void *u cfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (cfd < 0) { - if (IN_SET(errno, EAGAIN, EINTR)) + if (ERRNO_IS_ACCEPT_AGAIN(errno)) return 0; return -errno; } - r = dns_stream_new(m, &stream, DNS_PROTOCOL_LLMNR, cfd, NULL); + r = dns_stream_new(m, &stream, DNS_STREAM_LLMNR_RECV, DNS_PROTOCOL_LLMNR, cfd, NULL); if (r < 0) { safe_close(cfd); return r; } stream->on_packet = on_llmnr_stream_packet; + /* We don't configure a "complete" handler here, we rely on the default handler than simply drops the + * reference to the stream, thus freeing it */ return 0; } diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c index b3d35c834..9b8239bd0 100644 --- a/src/resolve/resolved-manager.c +++ b/src/resolve/resolved-manager.c @@ -1,9 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include #include #include +#include +#include +#include #if HAVE_LIBIDN2 #include diff --git a/src/resolve/resolved-manager.h b/src/resolve/resolved-manager.h index 06c76f601..72171f8c9 100644 --- a/src/resolve/resolved-manager.h +++ b/src/resolve/resolved-manager.h @@ -54,7 +54,7 @@ struct Manager { unsigned n_dns_queries; LIST_HEAD(DnsStream, dns_streams); - unsigned n_dns_streams; + unsigned n_dns_streams[_DNS_STREAM_TYPE_MAX]; /* Unicast dns */ LIST_HEAD(DnsServer, dns_servers); diff --git a/src/resolve/resolved-mdns.c b/src/resolve/resolved-mdns.c index 89c2497d3..67080cb01 100644 --- a/src/resolve/resolved-mdns.c +++ b/src/resolve/resolved-mdns.c @@ -8,6 +8,7 @@ #include "fd-util.h" #include "resolved-manager.h" #include "resolved-mdns.h" +#include "sort-util.h" #define CLEAR_CACHE_FLUSH(x) (~MDNS_RR_CACHE_FLUSH & (x)) diff --git a/src/resolve/resolved-resolv-conf.c b/src/resolve/resolved-resolv-conf.c index 5205071d3..0435791ea 100644 --- a/src/resolve/resolved-resolv-conf.c +++ b/src/resolve/resolved-resolv-conf.c @@ -2,6 +2,9 @@ #include #include +#include +#include +#include #include "alloc-util.h" #include "dns-domain.h" diff --git a/src/resolve/resolved.c b/src/resolve/resolved.c index 0845b2c1a..2ca9fbdc7 100644 --- a/src/resolve/resolved.c +++ b/src/resolve/resolved.c @@ -1,5 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "sd-daemon.h" #include "sd-event.h" diff --git a/src/resolve/test-resolved-etc-hosts.c b/src/resolve/test-resolved-etc-hosts.c index dbff889e3..ca3590066 100644 --- a/src/resolve/test-resolved-etc-hosts.c +++ b/src/resolve/test-resolved-etc-hosts.c @@ -10,6 +10,7 @@ #include "log.h" #include "resolved-etc-hosts.h" #include "strv.h" +#include "tests.h" #include "tmpfile-util.h" static void test_parse_etc_hosts_system(void) { @@ -137,9 +138,7 @@ static void test_parse_file(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); if (argc == 1) { test_parse_etc_hosts_system(); diff --git a/src/resolve/test-resolved-packet.c b/src/resolve/test-resolved-packet.c index 22ae26ce1..c9a511beb 100644 --- a/src/resolve/test-resolved-packet.c +++ b/src/resolve/test-resolved-packet.c @@ -2,6 +2,7 @@ #include "log.h" #include "resolved-dns-packet.h" +#include "tests.h" static void test_dns_packet_new(void) { size_t i; @@ -23,10 +24,7 @@ static void test_dns_packet_new(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_packet_new(); diff --git a/src/rfkill/rfkill.c b/src/rfkill/rfkill.c index ac21dc064..16610cd47 100644 --- a/src/rfkill/rfkill.c +++ b/src/rfkill/rfkill.c @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include +#include +#include +#include #include "sd-daemon.h" #include "sd-device.h" @@ -147,8 +151,8 @@ static int load_state(Context *c, const struct rfkill_event *event) { return r; r = read_one_line_file(state_file, &value); - if (r == -ENOENT) { - /* No state file? Then save the current state */ + if (IN_SET(r, -ENOENT, 0)) { + /* No state file or it's truncated? Then save the current state */ r = write_string_file(state_file, one_zero(event->soft), WRITE_STRING_FILE_CREATE|WRITE_STRING_FILE_ATOMIC); if (r < 0) diff --git a/src/run-generator/run-generator.c b/src/run-generator/run-generator.c index a5dfac01d..7d6c11091 100644 --- a/src/run-generator/run-generator.c +++ b/src/run-generator/run-generator.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "escape.h" #include "fd-util.h" diff --git a/src/run/run.c b/src/run/run.c index e9e31282b..56aa9aaee 100644 --- a/src/run/run.c +++ b/src/run/run.c @@ -2,6 +2,9 @@ #include #include +#include +#include +#include #include "sd-bus.h" #include "sd-event.h" @@ -10,6 +13,7 @@ #include "bus-error.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "bus-wait-for-jobs.h" #include "calendarspec.h" #include "env-util.h" #include "fd-util.h" @@ -56,7 +60,7 @@ static enum { static char **arg_path_property = NULL; static char **arg_socket_property = NULL; static char **arg_timer_property = NULL; -static bool with_timer = false; +static bool arg_with_timer = false; static bool arg_quiet = false; static bool arg_aggressive_gc = false; static char *arg_working_directory = NULL; @@ -120,6 +124,8 @@ static int help(void) { " --on-unit-active=SECONDS Run SECONDS after the last activation\n" " --on-unit-inactive=SECONDS Run SECONDS after the last deactivation\n" " --on-calendar=SPEC Realtime timer\n" + " --on-timezone-change Run when the timezone changes\n" + " --on-clock-change Run when the realtime clock jumps\n" " --timer-property=NAME=VALUE Set timer unit property\n" "\nSee the %s for details.\n" , program_invocation_short_name @@ -166,6 +172,8 @@ static int parse_argv(int argc, char *argv[]) { ARG_ON_UNIT_ACTIVE, ARG_ON_UNIT_INACTIVE, ARG_ON_CALENDAR, + ARG_ON_TIMEZONE_CHANGE, + ARG_ON_CLOCK_CHANGE, ARG_TIMER_PROPERTY, ARG_PATH_PROPERTY, ARG_SOCKET_PROPERTY, @@ -206,6 +214,8 @@ static int parse_argv(int argc, char *argv[]) { { "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 }, + { "on-timezone-change",no_argument, NULL, ARG_ON_TIMEZONE_CHANGE}, + { "on-clock-change", no_argument, NULL, ARG_ON_CLOCK_CHANGE }, { "timer-property", required_argument, NULL, ARG_TIMER_PROPERTY }, { "path-property", required_argument, NULL, ARG_PATH_PROPERTY }, { "socket-property", required_argument, NULL, ARG_SOCKET_PROPERTY }, @@ -335,7 +345,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - with_timer = true; + arg_with_timer = true; break; case ARG_ON_BOOT: @@ -343,7 +353,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - with_timer = true; + arg_with_timer = true; break; case ARG_ON_STARTUP: @@ -351,7 +361,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - with_timer = true; + arg_with_timer = true; break; case ARG_ON_UNIT_ACTIVE: @@ -359,7 +369,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - with_timer = true; + arg_with_timer = true; break; case ARG_ON_UNIT_INACTIVE: @@ -367,7 +377,7 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - with_timer = true; + arg_with_timer = true; break; case ARG_ON_CALENDAR: @@ -375,7 +385,23 @@ static int parse_argv(int argc, char *argv[]) { if (r < 0) return r; - with_timer = true; + arg_with_timer = true; + break; + + case ARG_ON_TIMEZONE_CHANGE: + r = add_timer_property("OnTimezoneChange", "yes"); + if (r < 0) + return r; + + arg_with_timer = true; + break; + + case ARG_ON_CLOCK_CHANGE: + r = add_timer_property("OnClockChange", "yes"); + if (r < 0) + return r; + + arg_with_timer = true; break; case ARG_TIMER_PROPERTY: @@ -383,7 +409,7 @@ static int parse_argv(int argc, char *argv[]) { if (strv_extend(&arg_timer_property, optarg) < 0) return log_oom(); - with_timer = with_timer || + arg_with_timer = arg_with_timer || STARTSWITH_SET(optarg, "OnActiveSec=", "OnBootSec=", @@ -451,10 +477,10 @@ static int parse_argv(int argc, char *argv[]) { assert_not_reached("Unhandled option"); } - with_trigger = !!arg_path_property || !!arg_socket_property || with_timer; + with_trigger = !!arg_path_property || !!arg_socket_property || arg_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) + if ((int) !!arg_path_property + (int) !!arg_socket_property + (int) arg_with_timer > 1) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Only single trigger (path, socket, timer) unit can be created."); @@ -550,7 +576,7 @@ static int parse_argv(int argc, char *argv[]) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path, socket or timer options are not supported in --scope mode."); - if (arg_timer_property && !with_timer) + if (arg_timer_property && !arg_with_timer) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--timer-property= has no effect without any other timer options."); @@ -1642,7 +1668,7 @@ static int run(int argc, char* argv[]) { r = start_transient_trigger(bus, ".path"); else if (arg_socket_property) r = start_transient_trigger(bus, ".socket"); - else if (with_timer) + else if (arg_with_timer) r = start_transient_trigger(bus, ".timer"); else r = start_transient_service(bus, &retval); diff --git a/src/shared/acl-util.c b/src/shared/acl-util.c index 963351451..1ccb4f829 100644 --- a/src/shared/acl-util.c +++ b/src/shared/acl-util.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include "acl-util.h" #include "alloc-util.h" diff --git a/src/shared/ask-password-api.c b/src/shared/ask-password-api.c index 072bf72c5..ab0c34692 100644 --- a/src/shared/ask-password-api.c +++ b/src/shared/ask-password-api.c @@ -31,6 +31,7 @@ #include "io-util.h" #include "log.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "mkdir.h" #include "process-util.h" @@ -44,7 +45,6 @@ #include "tmpfile-util.h" #include "umask-util.h" #include "utf8.h" -#include "util.h" #define KEYRING_TIMEOUT_USEC ((5 * USEC_PER_MINUTE) / 2) @@ -77,13 +77,18 @@ static int retrieve_key(key_serial_t serial, char ***ret) { n = keyctl(KEYCTL_READ, (unsigned long) serial, (unsigned long) p, (unsigned long) m, 0); if (n < 0) return -errno; - if (n < m) break; explicit_bzero_safe(p, n); - free(p); + + if (m > LONG_MAX / 2) /* overflow check */ + return -ENOMEM; m *= 2; + if ((long) (size_t) m != m) /* make sure that this still fits if converted to size_t */ + return -ENOMEM; + + free(p); } l = strv_parse_nulstr(p, n); @@ -306,9 +311,9 @@ int ask_password_tty( }; for (;;) { + _cleanup_(erase_char) char c; int sleep_for = -1, k; ssize_t n; - char c; if (until > 0) { usec_t y; @@ -385,13 +390,13 @@ int ask_password_tty( if (!(flags & ASK_PASSWORD_SILENT)) backspace_chars(ttyfd, 1); - /* Remove a full UTF-8 codepoint from the end. For that, figure out where the last one - * begins */ + /* Remove a full UTF-8 codepoint from the end. For that, figure out where the + * last one begins */ q = 0; for (;;) { size_t z; - z = utf8_encoded_valid_unichar(passphrase + q); + z = utf8_encoded_valid_unichar(passphrase + q, (size_t) -1); if (z == 0) { q = (size_t) -1; /* Invalid UTF8! */ break; @@ -410,8 +415,8 @@ int ask_password_tty( flags |= ASK_PASSWORD_SILENT; - /* There are two ways to enter silent mode. Either by pressing backspace as first key - * (and only as first key), or ... */ + /* There are two ways to enter silent mode. Either by pressing backspace as + * first key (and only as first key), or ... */ if (ttyfd >= 0) (void) loop_write(ttyfd, "(no echo) ", 10, false); @@ -440,18 +445,18 @@ int ask_password_tty( if (!(flags & ASK_PASSWORD_SILENT) && ttyfd >= 0) { /* Check if we got a complete UTF-8 character now. If so, let's output one '*'. */ - n = utf8_encoded_valid_unichar(passphrase + codepoint); + n = utf8_encoded_valid_unichar(passphrase + codepoint, (size_t) -1); if (n >= 0) { + if (flags & ASK_PASSWORD_ECHO) + (void) loop_write(ttyfd, passphrase + codepoint, n, false); + else + (void) loop_write(ttyfd, "*", 1, false); codepoint = p; - (void) loop_write(ttyfd, (flags & ASK_PASSWORD_ECHO) ? &c : "*", 1, false); } } dirty = true; } - - /* Let's forget this char, just to not keep needlessly copies of key material around */ - c = 'x'; } x = strndup(passphrase, p); diff --git a/src/shared/base-filesystem.c b/src/shared/base-filesystem.c index 89d7a7d59..657407da2 100644 --- a/src/shared/base-filesystem.c +++ b/src/shared/base-filesystem.c @@ -13,10 +13,11 @@ #include "fd-util.h" #include "log.h" #include "macro.h" +#include "nulstr-util.h" +#include "path-util.h" #include "string-util.h" #include "umask-util.h" #include "user-util.h" -#include "util.h" typedef struct BaseFilesystem { const char *dir; @@ -45,8 +46,8 @@ static const BaseFilesystem table[] = { int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { _cleanup_close_ int fd = -1; - int r = 0; size_t i; + int r; fd = open(root, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC|O_NOFOLLOW); if (fd < 0) @@ -68,7 +69,7 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { if (table[i].exists) { _cleanup_free_ char *p = NULL; - p = strjoin(s, "/", table[i].exists); + p = path_join(s, table[i].exists); if (!p) return log_oom(); @@ -83,9 +84,15 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { if (!target) continue; - r = symlinkat(target, fd, table[i].dir); - if (r < 0 && errno != EEXIST) - return log_error_errno(errno, "Failed to create symlink at %s/%s: %m", root, table[i].dir); + if (symlinkat(target, fd, table[i].dir) < 0) { + log_full_errno(IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno, + "Failed to create symlink at %s/%s: %m", root, table[i].dir); + + if (IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure) + continue; + + return -errno; + } if (uid_is_valid(uid) || gid_is_valid(gid)) { if (fchownat(fd, table[i].dir, uid, gid, AT_SYMLINK_NOFOLLOW) < 0) @@ -97,14 +104,14 @@ int base_filesystem_create(const char *root, uid_t uid, gid_t gid) { RUN_WITH_UMASK(0000) r = mkdirat(fd, table[i].dir, table[i].mode); - if (r < 0 && errno != EEXIST) { - log_full_errno(table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno, + if (r < 0) { + log_full_errno(IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure ? LOG_DEBUG : LOG_ERR, errno, "Failed to create directory at %s/%s: %m", root, table[i].dir); - if (!table[i].ignore_failure) - return -errno; + if (IN_SET(errno, EEXIST, EROFS) || table[i].ignore_failure) + continue; - continue; + return -errno; } if (uid != UID_INVALID || gid != UID_INVALID) { diff --git a/src/shared/bitmap.c b/src/shared/bitmap.c index a4cd6451b..a956a42ca 100644 --- a/src/shared/bitmap.c +++ b/src/shared/bitmap.c @@ -10,6 +10,7 @@ #include "bitmap.h" #include "hashmap.h" #include "macro.h" +#include "memory-util.h" struct Bitmap { uint64_t *bitmaps; diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index 7e276f1ed..9a0dc2915 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -2,7 +2,9 @@ #include #include +#include +#include "sd-device.h" #include "sd-id128.h" #include "alloc-util.h" @@ -11,15 +13,22 @@ #include "conf-files.h" #include "def.h" #include "device-nodes.h" +#include "dirent-util.h" #include "efivars.h" +#include "env-file.h" #include "env-util.h" #include "fd-util.h" #include "fileio.h" #include "parse-util.h" #include "path-util.h" +#include "pe-header.h" +#include "sort-util.h" #include "stat-util.h" +#include "string-table.h" #include "string-util.h" #include "strv.h" +#include "unaligned.h" +#include "util.h" #include "virt.h" static void boot_entry_free(BootEntry *entry) { @@ -27,6 +36,7 @@ static void boot_entry_free(BootEntry *entry) { free(entry->id); free(entry->path); + free(entry->root); free(entry->title); free(entry->show_title); free(entry->version); @@ -39,31 +49,44 @@ static void boot_entry_free(BootEntry *entry) { free(entry->device_tree); } -static int boot_entry_load(const char *path, BootEntry *entry) { - _cleanup_(boot_entry_free) BootEntry tmp = {}; +static int boot_entry_load( + const char *root, + const char *path, + BootEntry *entry) { + + _cleanup_(boot_entry_free) BootEntry tmp = { + .type = BOOT_ENTRY_CONF, + }; + _cleanup_fclose_ FILE *f = NULL; unsigned line = 1; char *b, *c; int r; + assert(root); assert(path); assert(entry); c = endswith_no_case(path, ".conf"); - if (!c) { - log_error("Invalid loader entry filename: %s", path); - return -EINVAL; - } + if (!c) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry file suffix: %s", path); b = basename(path); tmp.id = strndup(b, c - b); if (!tmp.id) return log_oom(); + if (!efi_loader_entry_name_valid(tmp.id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry filename: %s", path); + tmp.path = strdup(path); if (!tmp.path) return log_oom(); + tmp.root = strdup(root); + if (!tmp.root) + return log_oom(); + f = fopen(path, "re"); if (!f) return log_error_errno(errno, "Failed to open \"%s\": %m", path); @@ -115,7 +138,7 @@ static int boot_entry_load(const char *path, BootEntry *entry) { else if (streq(field, "devicetree")) r = free_and_strdup(&tmp.device_tree, p); else { - log_notice("%s:%u: Unknown line \"%s\"", path, line, field); + log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field); continue; } if (r < 0) @@ -137,6 +160,7 @@ void boot_config_free(BootConfig *config) { free(config->editor); free(config->auto_entries); free(config->auto_firmware); + free(config->console_mode); free(config->entry_oneshot); free(config->entry_default); @@ -203,7 +227,7 @@ static int boot_loader_read_conf(const char *path, BootConfig *config) { else if (streq(field, "console-mode")) r = free_and_strdup(&config->console_mode, p); else { - log_notice("%s:%u: Unknown line \"%s\"", path, line, field); + log_notice("%s:%u: Unknown line \"%s\", ignoring.", path, line, field); continue; } if (r < 0) @@ -217,36 +241,275 @@ static int boot_entry_compare(const BootEntry *a, const BootEntry *b) { return str_verscmp(a->id, b->id); } -static int boot_entries_find(const char *dir, BootEntry **ret_entries, size_t *ret_n_entries) { +static int boot_entries_find( + const char *root, + const char *dir, + BootEntry **entries, + size_t *n_entries) { + _cleanup_strv_free_ char **files = NULL; + size_t n_allocated = *n_entries; char **f; int r; - BootEntry *array = NULL; - size_t n_allocated = 0, n = 0; + assert(root); assert(dir); - assert(ret_entries); - assert(ret_n_entries); + assert(entries); + assert(n_entries); r = conf_files_list(&files, ".conf", NULL, 0, dir, NULL); if (r < 0) return log_error_errno(r, "Failed to list files in \"%s\": %m", dir); STRV_FOREACH(f, files) { - if (!GREEDY_REALLOC0(array, n_allocated, n + 1)) + if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1)) return log_oom(); - r = boot_entry_load(*f, array + n); + r = boot_entry_load(root, *f, *entries + *n_entries); if (r < 0) continue; - n++; + (*n_entries) ++; } - typesafe_qsort(array, n, boot_entry_compare); + return 0; +} - *ret_entries = array; - *ret_n_entries = n; +static int boot_entry_load_unified( + const char *root, + const char *path, + const char *osrelease, + const char *cmdline, + BootEntry *ret) { + + _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL; + _cleanup_(boot_entry_free) BootEntry tmp = { + .type = BOOT_ENTRY_UNIFIED, + }; + _cleanup_fclose_ FILE *f = NULL; + const char *k; + int r; + + assert(root); + assert(path); + assert(osrelease); + + k = path_startswith(path, root); + if (!k) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Path is not below root: %s", path); + + f = fmemopen((void*) osrelease, strlen(osrelease), "r"); + if (!f) + return log_error_errno(errno, "Failed to open os-release buffer: %m"); + + r = parse_env_file(f, "os-release", + "PRETTY_NAME", &os_pretty_name, + "ID", &os_id, + "VERSION_ID", &version_id, + "BUILD_ID", &build_id); + if (r < 0) + return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path); + + if (!os_pretty_name || !os_id || !(version_id || build_id)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path); + + tmp.id = strjoin(os_id, "-", version_id ?: build_id); + if (!tmp.id) + return log_oom(); + + if (!efi_loader_entry_name_valid(tmp.id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry: %s", tmp.id); + + tmp.path = strdup(path); + if (!tmp.path) + return log_oom(); + + tmp.root = strdup(root); + if (!tmp.root) + return log_oom(); + + tmp.kernel = strdup(skip_leading_chars(k, "/")); + if (!tmp.kernel) + return log_oom(); + + tmp.options = strv_new(skip_leading_chars(cmdline, WHITESPACE)); + if (!tmp.options) + return log_oom(); + + delete_trailing_chars(tmp.options[0], WHITESPACE); + + tmp.title = TAKE_PTR(os_pretty_name); + + *ret = tmp; + tmp = (BootEntry) {}; + return 0; +} + +/* Maximum PE section we are willing to load (Note that sections we are not interested in may be larger, but + * the ones we do care about and we are willing to load into memory have this size limit.) */ +#define PE_SECTION_SIZE_MAX (4U*1024U*1024U) + +static int find_sections( + int fd, + char **ret_osrelease, + char **ret_cmdline) { + + _cleanup_free_ struct PeSectionHeader *sections = NULL; + _cleanup_free_ char *osrelease = NULL, *cmdline = NULL; + size_t i, n_sections; + struct DosFileHeader dos; + struct PeHeader pe; + uint64_t start; + ssize_t n; + + n = pread(fd, &dos, sizeof(dos), 0); + if (n < 0) + return log_error_errno(errno, "Failed read DOS header: %m"); + if (n != sizeof(dos)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading DOS header, refusing."); + + if (dos.Magic[0] != 'M' || dos.Magic[1] != 'Z') + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "DOS executable magic missing, refusing."); + + start = unaligned_read_le32(&dos.ExeHeader); + n = pread(fd, &pe, sizeof(pe), start); + if (n < 0) + return log_error_errno(errno, "Failed to read PE header: %m"); + if (n != sizeof(pe)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading PE header, refusing."); + + if (pe.Magic[0] != 'P' || pe.Magic[1] != 'E' || pe.Magic[2] != 0 || pe.Magic[3] != 0) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE executable magic missing, refusing."); + + n_sections = unaligned_read_le16(&pe.FileHeader.NumberOfSections); + if (n_sections > 96) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "PE header has too many sections, refusing."); + + sections = new(struct PeSectionHeader, n_sections); + if (!sections) + return log_oom(); + + n = pread(fd, sections, + n_sections * sizeof(struct PeSectionHeader), + start + sizeof(pe) + unaligned_read_le16(&pe.FileHeader.SizeOfOptionalHeader)); + if (n < 0) + return log_error_errno(errno, "Failed to read section data: %m"); + if ((size_t) n != n_sections * sizeof(struct PeSectionHeader)) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading sections, refusing."); + + for (i = 0; i < n_sections; i++) { + _cleanup_free_ char *k = NULL; + uint32_t offset, size; + char **b; + + if (strneq((char*) sections[i].Name, ".osrel", sizeof(sections[i].Name))) + b = &osrelease; + else if (strneq((char*) sections[i].Name, ".cmdline", sizeof(sections[i].Name))) + b = &cmdline; + else + continue; + + if (*b) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Duplicate section %s, refusing.", sections[i].Name); + + offset = unaligned_read_le32(§ions[i].PointerToRawData); + size = unaligned_read_le32(§ions[i].VirtualSize); + + if (size > PE_SECTION_SIZE_MAX) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section %s too large, refusing.", sections[i].Name); + + k = new(char, size+1); + if (!k) + return log_oom(); + + n = pread(fd, k, size, offset); + if (n < 0) + return log_error_errno(errno, "Failed to read section payload: %m"); + if ((size_t) n != size) + return log_error_errno(SYNTHETIC_ERRNO(EIO), "Short read while reading section payload, refusing:"); + + /* Allow one trailing NUL byte, but nothing more. */ + if (size > 0 && memchr(k, 0, size - 1)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Section contains embedded NUL byte: %m"); + + k[size] = 0; + *b = TAKE_PTR(k); + } + + if (!osrelease) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Image lacks .osrel section, refusing."); + + if (ret_osrelease) + *ret_osrelease = TAKE_PTR(osrelease); + if (ret_cmdline) + *ret_cmdline = TAKE_PTR(cmdline); + + return 0; +} + +static int boot_entries_find_unified( + const char *root, + const char *dir, + BootEntry **entries, + size_t *n_entries) { + + _cleanup_(closedirp) DIR *d = NULL; + size_t n_allocated = *n_entries; + struct dirent *de; + int r; + + assert(root); + assert(dir); + assert(entries); + assert(n_entries); + + d = opendir(dir); + if (!d) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open %s: %m", dir); + } + + FOREACH_DIRENT(de, d, return log_error_errno(errno, "Failed to read %s: %m", dir)) { + _cleanup_free_ char *j = NULL, *osrelease = NULL, *cmdline = NULL; + _cleanup_close_ int fd = -1; + + if (!dirent_is_file(de)) + continue; + + if (!endswith_no_case(de->d_name, ".efi")) + continue; + + if (!GREEDY_REALLOC0(*entries, n_allocated, *n_entries + 1)) + return log_oom(); + + fd = openat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NONBLOCK); + if (fd < 0) { + log_warning_errno(errno, "Failed to open %s/%s, ignoring: %m", dir, de->d_name); + continue; + } + + r = fd_verify_regular(fd); + if (r < 0) { + log_warning_errno(r, "File %s/%s is not regular, ignoring: %m", dir, de->d_name); + continue; + } + + r = find_sections(fd, &osrelease, &cmdline); + if (r < 0) + continue; + + j = path_join(dir, de->d_name); + if (!j) + return log_oom(); + + r = boot_entry_load_unified(root, j, osrelease, cmdline, *entries + *n_entries); + if (r < 0) + continue; + + (*n_entries) ++; + } return 0; } @@ -325,6 +588,12 @@ static int boot_entries_select_default(const BootConfig *config) { int i; assert(config); + assert(config->entries || config->n_entries == 0); + + if (config->n_entries == 0) { + log_debug("Found no default boot entry :("); + return -1; /* -1 means "no default" */ + } if (config->entry_oneshot) for (i = config->n_entries - 1; i >= 0; i--) @@ -350,30 +619,50 @@ static int boot_entries_select_default(const BootConfig *config) { return i; } - if (config->n_entries > 0) - log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id); - else - log_debug("Found no default boot entry :("); - - return config->n_entries - 1; /* -1 means "no default" */ + log_debug("Found default: last entry \"%s\"", config->entries[config->n_entries - 1].id); + return config->n_entries - 1; } -int boot_entries_load_config(const char *esp_path, BootConfig *config) { +int boot_entries_load_config( + const char *esp_path, + const char *xbootldr_path, + BootConfig *config) { + const char *p; int r; - assert(esp_path); assert(config); - p = strjoina(esp_path, "/loader/loader.conf"); - r = boot_loader_read_conf(p, config); - if (r < 0) - return r; + if (esp_path) { + p = strjoina(esp_path, "/loader/loader.conf"); + r = boot_loader_read_conf(p, config); + if (r < 0) + return r; - p = strjoina(esp_path, "/loader/entries"); - r = boot_entries_find(p, &config->entries, &config->n_entries); - if (r < 0) - return r; + p = strjoina(esp_path, "/loader/entries"); + r = boot_entries_find(esp_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; + + p = strjoina(esp_path, "/EFI/Linux/"); + r = boot_entries_find_unified(esp_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; + } + + if (xbootldr_path) { + p = strjoina(xbootldr_path, "/loader/entries"); + r = boot_entries_find(xbootldr_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; + + p = strjoina(xbootldr_path, "/EFI/Linux/"); + r = boot_entries_find_unified(xbootldr_path, p, &config->entries, &config->n_entries); + if (r < 0) + return r; + } + + typesafe_qsort(config->entries, config->n_entries, boot_entry_compare); r = boot_entries_uniquify(config->entries, config->n_entries); if (r < 0) @@ -381,20 +670,394 @@ int boot_entries_load_config(const char *esp_path, BootConfig *config) { 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"); + if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) { + log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryOneShot\": %m"); + if (r == -ENOMEM) + return r; + } 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"); + if (r < 0 && !IN_SET(r, -ENOENT, -ENODATA)) { + log_warning_errno(r, "Failed to read EFI variable \"LoaderEntryDefault\": %m"); + if (r == -ENOMEM) + return r; + } } config->default_entry = boot_entries_select_default(config); return 0; } +int boot_entries_load_config_auto( + const char *override_esp_path, + const char *override_xbootldr_path, + BootConfig *config) { + + _cleanup_free_ char *esp_where = NULL, *xbootldr_where = NULL; + int r; + + assert(config); + + /* This function is similar to boot_entries_load_config(), however we automatically search for the + * ESP and the XBOOTLDR partition unless it is explicitly specified. Also, if the user did not pass + * an ESP or XBOOTLDR path directly, let's see if /run/boot-loader-entries/ exists. If so, let's + * read data from there, as if it was an ESP (i.e. loading both entries and loader.conf data from + * it). This allows other boot loaders to pass boot loader entry information to our tools if they + * want to. */ + + if (!override_esp_path && !override_xbootldr_path) { + if (access("/run/boot-loader-entries/", F_OK) >= 0) + return boot_entries_load_config("/run/boot-loader-entries/", NULL, config); + + if (errno != ENOENT) + return log_error_errno(errno, + "Failed to determine whether /run/boot-loader-entries/ exists: %m"); + } + + r = find_esp_and_warn(override_esp_path, false, &esp_where, NULL, NULL, NULL, NULL); + if (r < 0) /* we don't log about ENOKEY here, but propagate it, leaving it to the caller to log */ + return r; + + r = find_xbootldr_and_warn(override_xbootldr_path, false, &xbootldr_where, NULL); + if (r < 0 && r != -ENOKEY) + return r; /* It's fine if the XBOOTLDR partition doesn't exist, hence we ignore ENOKEY here */ + + return boot_entries_load_config(esp_where, xbootldr_where, config); +} + +int boot_entries_augment_from_loader(BootConfig *config, bool only_auto) { + + static const char * const title_table[] = { + /* Pretty names for a few well-known automatically discovered entries. */ + "auto-osx", "macOS", + "auto-windows", "Windows Boot Manager", + "auto-efi-shell", "EFI Shell", + "auto-efi-default", "EFI Default Loader", + "auto-reboot-to-firmware-setup", "Reboot Into Firmware Interface", + }; + + _cleanup_strv_free_ char **found_by_loader = NULL; + size_t n_allocated; + char **i; + int r; + + assert(config); + + /* Let's add the entries discovered by the boot loader to the end of our list, unless they are + * already included there. */ + + r = efi_loader_get_entries(&found_by_loader); + if (IN_SET(r, -ENOENT, -EOPNOTSUPP)) + return log_debug_errno(r, "Boot loader reported no entries."); + if (r < 0) + return log_error_errno(r, "Failed to determine entries reported by boot loader: %m"); + + n_allocated = config->n_entries; + + STRV_FOREACH(i, found_by_loader) { + _cleanup_free_ char *c = NULL, *t = NULL; + char **a, **b; + + if (boot_config_has_entry(config, *i)) + continue; + + if (only_auto && !startswith(*i, "auto-")) + continue; + + c = strdup(*i); + if (!c) + return log_oom(); + + STRV_FOREACH_PAIR(a, b, (char**) title_table) + if (streq(*a, *i)) { + t = strdup(*b); + if (!t) + return log_oom(); + break; + } + + if (!GREEDY_REALLOC0(config->entries, n_allocated, config->n_entries + 1)) + return log_oom(); + + config->entries[config->n_entries++] = (BootEntry) { + .type = BOOT_ENTRY_LOADER, + .id = TAKE_PTR(c), + .title = TAKE_PTR(t), + }; + } + + return 0; +} + /********************************************************************************/ +static int verify_esp_blkid( + dev_t devid, + bool searching, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &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(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); + + blkid_probe_enable_superblocks(b, 1); + blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); + + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system type of \"%s\": %m", node); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition number of \"%s\": m", node); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition offset of \"%s\": %m", node); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition size of \"%s\": %m", node); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); +#endif + + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_esp_udev( + dev_t devid, + bool searching, + uint32_t *ret_part, + uint64_t *ret_pstart, + uint64_t *ret_psize, + sd_id128_t *ret_uuid) { + + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *node = NULL; + sd_id128_t uuid = SD_ID128_NULL; + uint64_t pstart = 0, psize = 0; + uint32_t part = 0; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + r = sd_device_new_from_devnum(&d, 'b', devid); + if (r < 0) + return log_error_errno(r, "Failed to get device from device number: %m"); + + r = sd_device_get_property_value(d, "ID_FS_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "vfat")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not FAT.", node ); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "gpt")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not on a GPT partition table.", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" has wrong type for an EFI System Partition (ESP).", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_NUMBER", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou32(v, &part); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_OFFSET", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou64(v, &pstart); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SIZE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = safe_atou64(v, &psize); + if (r < 0) + return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); + + if (ret_part) + *ret_part = part; + if (ret_pstart) + *ret_pstart = pstart; + if (ret_psize) + *ret_psize = psize; + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_fsroot_dir( + const char *path, + bool searching, + bool unprivileged_mode, + dev_t *ret_dev) { + + struct stat st, st2; + const char *t2, *trigger; + int r; + + assert(path); + assert(ret_dev); + + /* So, the ESP and XBOOTLDR partition are commonly located on an autofs mount. stat() on the + * directory won't trigger it, if it is not mounted yet. Let's hence explicitly trigger it here, + * before stat()ing */ + trigger = strjoina(path, "/trigger"); /* Filename doesn't matter... */ + (void) access(trigger, F_OK); + + if (stat(path, &st) < 0) + return log_full_errno((searching && errno == ENOENT) || + (unprivileged_mode && errno == EACCES) ? LOG_DEBUG : LOG_ERR, errno, + "Failed to determine block device node of \"%s\": %m", path); + + if (major(st.st_dev) == 0) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Block device node of \"%s\" is invalid.", path); + + t2 = strjoina(path, "/.."); + if (stat(t2, &st2) < 0) { + if (errno != EACCES) + r = -errno; + else { + _cleanup_free_ char *parent = NULL; + + /* If going via ".." didn't work due to EACCESS, then let's determine the parent path + * directly instead. It's not as good, due to symlinks and such, but we can't do + * anything better here. */ + + parent = dirname_malloc(path); + if (!parent) + return log_oom(); + + if (stat(parent, &st2) < 0) + r = -errno; + else + r = 0; + } + + if (r < 0) + return log_full_errno(unprivileged_mode && r == -EACCES ? LOG_DEBUG : LOG_ERR, r, + "Failed to determine block device node of parent of \"%s\": %m", path); + } + + if (st.st_dev == st2.st_dev) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "Directory \"%s\" is not the root of the file system.", path); + + if (ret_dev) + *ret_dev = st.st_dev; + + return 0; +} + static int verify_esp( const char *p, bool searching, @@ -403,167 +1066,68 @@ static int verify_esp( uint64_t *ret_pstart, uint64_t *ret_psize, sd_id128_t *ret_uuid) { -#if HAVE_BLKID - _cleanup_(blkid_free_probep) blkid_probe b = NULL; - _cleanup_free_ char *node = NULL; - const char *v; -#endif - uint64_t pstart = 0, psize = 0; - struct stat st, st2; - const char *t2; - struct statfs sfs; - sd_id128_t uuid = SD_ID128_NULL; - uint32_t part = 0; + bool relax_checks; + dev_t devid; int r; assert(p); + /* This logs about all errors, except: + * + * -ENOENT → if 'searching' is set, and the dir doesn't exist + * -EADDRNOTAVAIL → if 'searching' is set, and the dir doesn't look like an ESP + * -EACESS → if 'unprivileged_mode' is set, and we have trouble acessing the thing + */ + 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 (!relax_checks) { - if (statfs(p, &sfs) < 0) { + struct statfs sfs; + + 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, + return log_full_errno((searching && errno == ENOENT) || + (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; - - log_error("File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); - return -ENODEV; - } + if (!F_TYPE_EQUAL(sfs.f_type, MSDOS_SUPER_MAGIC)) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + SYNTHETIC_ERRNO(searching ? EADDRNOTAVAIL : ENODEV), + "File system \"%s\" is not a FAT EFI System Partition (ESP) file system.", p); } - if (stat(p, &st) < 0) - return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of \"%s\": %m", p); - - if (major(st.st_dev) == 0) { - log_error("Block device node of %p is invalid.", p); - return -ENODEV; - } - - t2 = strjoina(p, "/.."); - r = stat(t2, &st2); + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); if (r < 0) - return log_full_errno(unprivileged_mode && errno == EACCES ? LOG_DEBUG : LOG_ERR, errno, - "Failed to determine block device node of parent of \"%s\": %m", p); + return r; - if (st.st_dev == st2.st_dev) { - log_error("Directory \"%s\" is not the root of the EFI System Partition (ESP) file system.", p); - return -ENODEV; - } - - /* 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 || relax_checks) + /* 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. */ + if (detect_container() > 0 || relax_checks) goto finish; -#if HAVE_BLKID - 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(node); - if (!b) - return log_error_errno(errno ?: ENOMEM, "Failed to open file system \"%s\": %m", p); - - blkid_probe_enable_superblocks(b, 1); - blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE); - blkid_probe_enable_partitions(b, 1); - blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - - errno = 0; - r = blkid_do_safeprobe(b); - if (r == -2) { - log_error("File system \"%s\" is ambiguous.", p); - return -ENODEV; - } else if (r == 1) { - log_error("File system \"%s\" does not contain a label.", p); - return -ENODEV; - } else if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe file system \"%s\": %m", p); - - errno = 0; - r = blkid_probe_lookup_value(b, "TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe file system type \"%s\": %m", p); - if (!streq(v, "vfat")) { - log_error("File system \"%s\" is not FAT.", p); - return -ENODEV; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition scheme \"%s\": %m", p); - if (!streq(v, "gpt")) { - log_error("File system \"%s\" is not on a GPT partition table.", p); - return -ENODEV; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition type UUID \"%s\": %m", p); - if (!streq(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b")) { - log_error("File system \"%s\" has wrong type for an EFI System Partition (ESP).", p); - return -ENODEV; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition entry UUID \"%s\": %m", p); - r = sd_id128_from_string(v, &uuid); - if (r < 0) { - log_error("Partition \"%s\" has invalid UUID \"%s\".", p, v); - return -EIO; - } - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition number \"%s\": m", p); - r = safe_atou32(v, &part); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_NUMBER field."); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition offset \"%s\": %m", p); - r = safe_atou64(v, &pstart); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_OFFSET field."); - - errno = 0; - r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL); - if (r != 0) - return log_error_errno(errno ?: EIO, "Failed to probe partition size \"%s\": %m", p); - r = safe_atou64(v, &psize); - if (r < 0) - return log_error_errno(r, "Failed to parse PART_ENTRY_SIZE field."); -#endif + /* If we are unprivileged we ask udev for the metadata about the partition. If we are privileged we + * use blkid instead. Why? Because this code is called from 'bootctl' which is pretty much an + * emergency recovery tool that should also work when udev isn't up (i.e. from the emergency shell), + * however blkid can't work if we have no privileges to access block devices directly, which is why + * we use udev in that case. */ + if (unprivileged_mode) + return verify_esp_udev(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); + else + return verify_esp_blkid(devid, searching, ret_part, ret_pstart, ret_psize, ret_uuid); finish: if (ret_part) - *ret_part = part; + *ret_part = 0; if (ret_pstart) - *ret_pstart = pstart; + *ret_pstart = 0; if (ret_psize) - *ret_psize = psize; + *ret_psize = 0; if (ret_uuid) - *ret_uuid = uuid; + *ret_uuid = SD_ID128_NULL; return 0; } @@ -631,35 +1195,234 @@ found: return 0; } -int find_default_boot_entry( - const char *esp_path, - char **esp_where, - BootConfig *config, - const BootEntry **e) { +static int verify_xbootldr_blkid( + dev_t devid, + bool searching, + sd_id128_t *ret_uuid) { - _cleanup_free_ char *where = NULL; + sd_id128_t uuid = SD_ID128_NULL; + +#if HAVE_BLKID + _cleanup_(blkid_free_probep) blkid_probe b = NULL; + _cleanup_free_ char *node = NULL; + const char *v; int r; - assert(config); - assert(e); - - r = find_esp_and_warn(esp_path, false, &where, NULL, NULL, NULL, NULL); + r = device_path_make_major_minor(S_IFBLK, devid, &node); if (r < 0) - return r; + return log_error_errno(r, "Failed to format major/minor device path: %m"); + errno = 0; + b = blkid_new_probe_from_filename(node); + if (!b) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(ENOMEM), "Failed to open file system \"%s\": %m", node); - 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); + blkid_probe_enable_partitions(b, 1); + blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS); - if (config->default_entry < 0) - return log_error_errno(SYNTHETIC_ERRNO(ENOENT), - "No entry suitable as default, refusing to guess."); + errno = 0; + r = blkid_do_safeprobe(b); + if (r == -2) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" is ambiguous.", node); + else if (r == 1) + return log_error_errno(SYNTHETIC_ERRNO(ENODEV), "File system \"%s\" does not contain a label.", node); + else if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe file system \"%s\": %m", node); - *e = &config->entries[config->default_entry]; - log_debug("Found default boot entry in file \"%s\"", (*e)->path); + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition scheme of \"%s\": %m", node); + if (streq(v, "gpt")) { - if (esp_where) - *esp_where = TAKE_PTR(where); + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition entry UUID of \"%s\": %m", node); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + } else if (streq(v, "dos")) { + + errno = 0; + r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL); + if (r != 0) + return log_error_errno(errno ?: SYNTHETIC_ERRNO(EIO), "Failed to probe partition type UUID of \"%s\": %m", node); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", node); +#endif + + if (ret_uuid) + *ret_uuid = uuid; return 0; } + +static int verify_xbootldr_udev( + dev_t devid, + bool searching, + sd_id128_t *ret_uuid) { + + _cleanup_(sd_device_unrefp) sd_device *d = NULL; + _cleanup_free_ char *node = NULL; + sd_id128_t uuid = SD_ID128_NULL; + const char *v; + int r; + + r = device_path_make_major_minor(S_IFBLK, devid, &node); + if (r < 0) + return log_error_errno(r, "Failed to format major/minor device path: %m"); + + r = sd_device_new_from_devnum(&d, 'b', devid); + if (r < 0) + return log_error_errno(r, "Failed to get device from device number: %m"); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_SCHEME", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + + if (streq(v, "gpt")) { + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "bc13c2ff-59e6-4262-a352-b275fd6f7172")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_UUID", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + r = sd_id128_from_string(v, &uuid); + if (r < 0) + return log_error_errno(r, "Partition \"%s\" has invalid UUID \"%s\".", node, v); + + } else if (streq(v, "dos")) { + + r = sd_device_get_property_value(d, "ID_PART_ENTRY_TYPE", &v); + if (r < 0) + return log_error_errno(r, "Failed to get device property: %m"); + if (!streq(v, "0xea")) + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" has wrong type for extended boot loader partition.", node); + } else + return log_full_errno(searching ? LOG_DEBUG : LOG_ERR, + searching ? SYNTHETIC_ERRNO(EADDRNOTAVAIL) : SYNTHETIC_ERRNO(ENODEV), + "File system \"%s\" is not on a GPT or DOS partition table.", node); + + if (ret_uuid) + *ret_uuid = uuid; + + return 0; +} + +static int verify_xbootldr( + const char *p, + bool searching, + bool unprivileged_mode, + sd_id128_t *ret_uuid) { + + bool relax_checks; + dev_t devid; + int r; + + assert(p); + + relax_checks = getenv_bool("SYSTEMD_RELAX_XBOOTLDR_CHECKS") > 0; + + r = verify_fsroot_dir(p, searching, unprivileged_mode, &devid); + if (r < 0) + return r; + + if (detect_container() > 0 || relax_checks) + goto finish; + + if (unprivileged_mode) + return verify_xbootldr_udev(devid, searching, ret_uuid); + else + return verify_xbootldr_blkid(devid, searching, ret_uuid); + +finish: + if (ret_uuid) + *ret_uuid = SD_ID128_NULL; + + return 0; +} + +int find_xbootldr_and_warn( + const char *path, + bool unprivileged_mode, + char **ret_path, + sd_id128_t *ret_uuid) { + + int r; + + /* Similar to find_esp_and_warn(), but finds the XBOOTLDR partition. Returns the same errors. */ + + if (path) { + r = verify_xbootldr(path, false, unprivileged_mode, ret_uuid); + if (r < 0) + return r; + + goto found; + } + + path = getenv("SYSTEMD_XBOOTLDR_PATH"); + if (path) { + if (!path_is_valid(path) || !path_is_absolute(path)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "$SYSTEMD_XBOOTLDR_PATH does not refer to absolute path, refusing to use it: %s", + path); + + goto found; + } + + r = verify_xbootldr("/boot", true, unprivileged_mode, ret_uuid); + if (r >= 0) { + path = "/boot"; + goto found; + } + if (!IN_SET(r, -ENOENT, -EADDRNOTAVAIL)) /* This one is not it */ + return r; + + return -ENOKEY; + +found: + if (ret_path) { + char *c; + + c = strdup(path); + if (!c) + return log_oom(); + + *ret_path = c; + } + + return 0; +} + +static const char* const boot_entry_type_table[_BOOT_ENTRY_MAX] = { + [BOOT_ENTRY_CONF] = "conf", + [BOOT_ENTRY_UNIFIED] = "unified", + [BOOT_ENTRY_LOADER] = "loader", +}; + +DEFINE_STRING_TABLE_LOOKUP(boot_entry_type, BootEntryType); diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index ed576210f..45e1fc37c 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -8,9 +8,21 @@ #include "sd-id128.h" +#include "string-util.h" + +typedef enum BootEntryType { + BOOT_ENTRY_CONF, /* Type #1 entries: *.conf files */ + BOOT_ENTRY_UNIFIED, /* Type #2 entries: *.efi files */ + BOOT_ENTRY_LOADER, /* Additional entries augmented from LoaderEntries EFI var */ + _BOOT_ENTRY_MAX, + _BOOT_ENTRY_INVALID = -1, +} BootEntryType; + typedef struct BootEntry { + BootEntryType type; char *id; /* This is the file basename without extension */ - char *path; /* This is the full path to the file */ + char *path; /* This is the full path to the drop-in file */ + char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */ char *title; char *show_title; char *version; @@ -39,13 +51,34 @@ typedef struct BootConfig { ssize_t default_entry; } BootConfig; +static inline bool boot_config_has_entry(BootConfig *config, const char *id) { + size_t j; + + for (j = 0; j < config->n_entries; j++) + if (streq(config->entries[j].id, id)) + return true; + + return false; +} + +static inline BootEntry* boot_config_default_entry(BootConfig *config) { + if (config->default_entry < 0) + return NULL; + + return config->entries + config->default_entry; +} + void boot_config_free(BootConfig *config); -int boot_entries_load_config(const char *esp_path, BootConfig *config); +int boot_entries_load_config(const char *esp_path, const char *xbootldr_path, BootConfig *config); +int boot_entries_load_config_auto(const char *override_esp_path, const char *override_xbootldr_path, BootConfig *config); +int boot_entries_augment_from_loader(BootConfig *config, bool only_auto); static inline const char* boot_entry_title(const BootEntry *entry) { 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_xbootldr_and_warn(const char *path, bool unprivileged_mode, char **ret_path,sd_id128_t *ret_uuid); -int find_default_boot_entry(const char *esp_path, char **esp_where, BootConfig *config, const BootEntry **e); +const char* boot_entry_type_to_string(BootEntryType t) _const_; +BootEntryType boot_entry_type_from_string(const char *s) _pure_; diff --git a/src/shared/bpf-program.c b/src/shared/bpf-program.c index 2c61e0413..40bc9645b 100644 --- a/src/shared/bpf-program.c +++ b/src/shared/bpf-program.c @@ -9,9 +9,9 @@ #include "bpf-program.h" #include "fd-util.h" #include "log.h" +#include "memory-util.h" #include "missing.h" #include "path-util.h" -#include "util.h" int bpf_program_new(uint32_t prog_type, BPFProgram **ret) { _cleanup_(bpf_program_unrefp) BPFProgram *p = NULL; diff --git a/src/shared/bus-unit-procs.c b/src/shared/bus-unit-procs.c new file mode 100644 index 000000000..23c5a1245 --- /dev/null +++ b/src/shared/bus-unit-procs.c @@ -0,0 +1,411 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "bus-unit-procs.h" +#include "hashmap.h" +#include "list.h" +#include "locale-util.h" +#include "macro.h" +#include "path-util.h" +#include "process-util.h" +#include "sort-util.h" +#include "string-util.h" +#include "terminal-util.h" + +struct CGroupInfo { + char *cgroup_path; + bool is_const; /* If false, cgroup_path should be free()'d */ + + Hashmap *pids; /* PID → process name */ + bool done; + + struct CGroupInfo *parent; + LIST_FIELDS(struct CGroupInfo, siblings); + LIST_HEAD(struct CGroupInfo, children); + size_t n_children; +}; + +static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { + struct CGroupInfo *parent = NULL, *cg; + int r; + + assert(cgroups); + assert(ret); + + if (empty_or_root(path)) + path = "/"; + + cg = hashmap_get(cgroups, path); + if (cg) { + *ret = cg; + return 0; + } + + if (!empty_or_root(path)) { + const char *e, *pp; + + e = strrchr(path, '/'); + if (!e) + return -EINVAL; + + pp = strndupa(path, e - path); + if (!pp) + return -ENOMEM; + + r = add_cgroup(cgroups, pp, false, &parent); + if (r < 0) + return r; + } + + cg = new0(struct CGroupInfo, 1); + if (!cg) + return -ENOMEM; + + if (is_const) + cg->cgroup_path = (char*) path; + else { + cg->cgroup_path = strdup(path); + if (!cg->cgroup_path) { + free(cg); + return -ENOMEM; + } + } + + cg->is_const = is_const; + cg->parent = parent; + + r = hashmap_put(cgroups, cg->cgroup_path, cg); + if (r < 0) { + if (!is_const) + free(cg->cgroup_path); + free(cg); + return r; + } + + if (parent) { + LIST_PREPEND(siblings, parent->children, cg); + parent->n_children++; + } + + *ret = cg; + return 1; +} + +static int add_process( + Hashmap *cgroups, + const char *path, + pid_t pid, + const char *name) { + + struct CGroupInfo *cg; + int r; + + assert(cgroups); + assert(name); + assert(pid > 0); + + r = add_cgroup(cgroups, path, true, &cg); + if (r < 0) + return r; + + r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); + if (r < 0) + return r; + + return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); +} + +static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { + assert(cgroups); + assert(cg); + + while (cg->children) + remove_cgroup(cgroups, cg->children); + + hashmap_remove(cgroups, cg->cgroup_path); + + if (!cg->is_const) + free(cg->cgroup_path); + + hashmap_free(cg->pids); + + if (cg->parent) + LIST_REMOVE(siblings, cg->parent->children, cg); + + free(cg); +} + +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( + Hashmap *cgroups, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + struct CGroupInfo *cg; + int r; + + assert(prefix); + + if (empty_or_root(cgroup_path)) + cgroup_path = "/"; + + cg = hashmap_get(cgroups, cgroup_path); + if (!cg) + return 0; + + if (!hashmap_isempty(cg->pids)) { + const char *name; + size_t n = 0, i; + pid_t *pids; + void *pidp; + Iterator j; + int width; + + /* Order processes by their PID */ + pids = newa(pid_t, hashmap_size(cg->pids)); + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) + pids[n++] = PTR_TO_PID(pidp); + + assert(n == hashmap_size(cg->pids)); + typesafe_qsort(pids, n, pid_compare_func); + + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *e = NULL; + const char *special; + bool more; + + name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); + assert(name); + + if (n_columns != 0) { + unsigned k; + + k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, k, 100); + if (e) + name = e; + } + + more = i+1 < n || cg->children; + special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT); + + fprintf(stdout, "%s%s%*"PID_PRI" %s\n", + prefix, + special, + width, pids[i], + name); + } + } + + if (cg->children) { + struct CGroupInfo **children, *child; + size_t n = 0, i; + + /* Order subcgroups by their name */ + children = newa(struct CGroupInfo*, cg->n_children); + LIST_FOREACH(siblings, child, cg->children) + children[n++] = child; + assert(n == cg->n_children); + typesafe_qsort(children, n, cgroup_info_compare_func); + + if (n_columns != 0) + n_columns = MAX(LESS_BY(n_columns, 2U), 20U); + + for (i = 0; i < n; i++) { + _cleanup_free_ char *pp = NULL; + const char *name, *special; + bool more; + + child = children[i]; + + name = strrchr(child->cgroup_path, '/'); + if (!name) + return -EINVAL; + name++; + + more = i+1 < n; + 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 ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE); + + pp = strappend(prefix, special); + if (!pp) + return -ENOMEM; + + r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); + if (r < 0) + return r; + } + } + + cg->done = true; + return 0; +} + +static int dump_extra_processes( + Hashmap *cgroups, + const char *prefix, + unsigned n_columns, + OutputFlags flags) { + + _cleanup_free_ pid_t *pids = NULL; + _cleanup_hashmap_free_ Hashmap *names = NULL; + struct CGroupInfo *cg; + size_t n_allocated = 0, n = 0, k; + Iterator i; + int width, r; + + /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as + * combined, sorted, linear list. */ + + HASHMAP_FOREACH(cg, cgroups, i) { + const char *name; + void *pidp; + Iterator j; + + if (cg->done) + continue; + + if (hashmap_isempty(cg->pids)) + continue; + + r = hashmap_ensure_allocated(&names, &trivial_hash_ops); + if (r < 0) + return r; + + if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) + return -ENOMEM; + + HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { + pids[n++] = PTR_TO_PID(pidp); + + r = hashmap_put(names, pidp, (void*) name); + if (r < 0) + return r; + } + } + + if (n == 0) + return 0; + + typesafe_qsort(pids, n, pid_compare_func); + width = DECIMAL_STR_WIDTH(pids[n-1]); + + for (k = 0; k < n; k++) { + _cleanup_free_ char *e = NULL; + const char *name; + + name = hashmap_get(names, PID_TO_PTR(pids[k])); + assert(name); + + if (n_columns != 0) { + unsigned z; + + z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); + + e = ellipsize(name, z, 100); + if (e) + name = e; + } + + fprintf(stdout, "%s%s %*" PID_PRI " %s\n", + prefix, + special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), + width, pids[k], + name); + } + + return 0; +} + +int unit_show_processes( + sd_bus *bus, + const char *unit, + const char *cgroup_path, + const char *prefix, + unsigned n_columns, + OutputFlags flags, + sd_bus_error *error) { + + _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; + Hashmap *cgroups = NULL; + struct CGroupInfo *cg; + int r; + + assert(bus); + assert(unit); + + if (flags & OUTPUT_FULL_WIDTH) + n_columns = 0; + else if (n_columns <= 0) + n_columns = columns(); + + prefix = strempty(prefix); + + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetUnitProcesses", + error, + &reply, + "s", + unit); + if (r < 0) + return r; + + cgroups = hashmap_new(&path_hash_ops); + if (!cgroups) + return -ENOMEM; + + r = sd_bus_message_enter_container(reply, 'a', "(sus)"); + if (r < 0) + goto finish; + + for (;;) { + const char *path = NULL, *name = NULL; + uint32_t pid; + + r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); + if (r < 0) + goto finish; + if (r == 0) + break; + + r = add_process(cgroups, path, pid, name); + if (r < 0) + goto finish; + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto finish; + + r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); + if (r < 0) + goto finish; + + r = dump_extra_processes(cgroups, prefix, n_columns, flags); + +finish: + while ((cg = hashmap_first(cgroups))) + remove_cgroup(cgroups, cg); + + hashmap_free(cgroups); + + return r; +} diff --git a/src/shared/bus-unit-procs.h b/src/shared/bus-unit-procs.h new file mode 100644 index 000000000..1cb5ca627 --- /dev/null +++ b/src/shared/bus-unit-procs.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" + +#include "output-mode.h" + +int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c index 9a8051d06..b42a9e5c5 100644 --- a/src/shared/bus-unit-util.c +++ b/src/shared/bus-unit-util.c @@ -1,39 +1,36 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" -#include "bus-internal.h" +#include "bus-error.h" #include "bus-unit-util.h" #include "bus-util.h" #include "cap-list.h" #include "cgroup-util.h" #include "condition.h" #include "cpu-set-util.h" -#include "env-util.h" -#include "errno-list.h" #include "escape.h" -#include "hashmap.h" #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 "log.h" #include "missing_fs.h" #include "mountpoint-util.h" #include "nsflags.h" #include "parse-util.h" -#include "path-util.h" #include "process-util.h" #include "rlimit-util.h" #include "securebits-util.h" #include "signal-util.h" +#include "socket-util.h" +#include "sort-util.h" #include "string-util.h" #include "syslog-util.h" #include "terminal-util.h" #include "unit-def.h" #include "user-util.h" #include "utf8.h" -#include "util.h" int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u) { assert(message); @@ -464,6 +461,20 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons return 1; } + if (streq(field, "CPUQuotaPeriodSec")) { + usec_t u = USEC_INFINITY; + + r = parse_sec_def_infinity(eq, &u); + if (r < 0) + return log_error_errno(r, "CPU quota period '%s' invalid.", eq); + + r = sd_bus_message_append(m, "(sv)", "CPUQuotaPeriodUSec", "t", u); + if (r < 0) + return bus_log_create_error(r); + + return 1; + } + if (streq(field, "DeviceAllow")) { if (isempty(eq)) @@ -730,17 +741,17 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con "UtmpIdentifier", "UtmpMode", "PAMName", "TTYPath", "WorkingDirectory", "RootDirectory", "SyslogIdentifier", "ProtectSystem", "ProtectHome", "SELinuxContext", "RootImage", - "RuntimeDirectoryPreserve", "Personality", "KeyringMode")) + "RuntimeDirectoryPreserve", "Personality", "KeyringMode", "NetworkNamespacePath")) return bus_append_string(m, field, eq); if (STR_IN_SET(field, - "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", - "PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", - "PrivateMounts", "NoNewPrivileges", "SyslogLevelPrefix", - "MemoryDenyWriteExecute", "RestrictRealtime", "DynamicUser", "RemoveIPC", - "ProtectKernelTunables", "ProtectKernelModules", "ProtectControlGroups", - "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality")) + "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "TTYVTDisallocate", "PrivateTmp", + "PrivateDevices", "PrivateNetwork", "PrivateUsers", "PrivateMounts", + "NoNewPrivileges", "SyslogLevelPrefix", "MemoryDenyWriteExecute", "RestrictRealtime", + "DynamicUser", "RemoveIPC", "ProtectKernelTunables", "ProtectKernelModules", + "ProtectControlGroups", "MountAPIVFS", "CPUSchedulingResetOnFork", "LockPersonality", + "ProtectHostname", "RestrictSUIDSGID")) return bus_append_parse_boolean(m, field, eq); @@ -1488,7 +1499,8 @@ static int bus_append_socket_property(sd_bus_message *m, const char *field, cons static int bus_append_timer_property(sd_bus_message *m, const char *field, const char *eq) { int r; - if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent")) + if (STR_IN_SET(field, "WakeSystem", "RemainAfterElapse", "Persistent", + "OnTimezoneChange", "OnClockChange")) return bus_append_parse_boolean(m, field, eq); @@ -1758,327 +1770,6 @@ int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char return 0; } -typedef struct BusWaitForJobs { - sd_bus *bus; - Set *jobs; - - char *name; - char *result; - - sd_bus_slot *slot_job_removed; - sd_bus_slot *slot_disconnected; -} BusWaitForJobs; - -static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { - assert(m); - - log_error("Warning! D-Bus connection terminated."); - sd_bus_close(sd_bus_message_get_bus(m)); - - return 0; -} - -static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { - const char *path, *unit, *result; - BusWaitForJobs *d = userdata; - uint32_t id; - char *found; - int r; - - assert(m); - assert(d); - - r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); - if (r < 0) { - bus_log_parse_error(r); - return 0; - } - - found = set_remove(d->jobs, (char*) path); - if (!found) - return 0; - - free(found); - - if (!isempty(result)) - d->result = strdup(result); - - if (!isempty(unit)) - d->name = strdup(unit); - - return 0; -} - -void bus_wait_for_jobs_free(BusWaitForJobs *d) { - if (!d) - return; - - set_free_free(d->jobs); - - sd_bus_slot_unref(d->slot_disconnected); - sd_bus_slot_unref(d->slot_job_removed); - - sd_bus_unref(d->bus); - - free(d->name); - free(d->result); - - free(d); -} - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { - _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; - int r; - - assert(bus); - assert(ret); - - d = new0(BusWaitForJobs, 1); - if (!d) - return -ENOMEM; - - d->bus = sd_bus_ref(bus); - - /* When we are a bus client we match by sender. Direct - * connections OTOH have no initialized sender field, and - * hence we ignore the sender then */ - r = sd_bus_match_signal_async( - bus, - &d->slot_job_removed, - bus->bus_client ? "org.freedesktop.systemd1" : NULL, - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "JobRemoved", - match_job_removed, NULL, d); - if (r < 0) - return r; - - r = sd_bus_match_signal_async( - bus, - &d->slot_disconnected, - "org.freedesktop.DBus.Local", - NULL, - "org.freedesktop.DBus.Local", - "Disconnected", - match_disconnected, NULL, d); - if (r < 0) - return r; - - *ret = TAKE_PTR(d); - - return 0; -} - -static int bus_process_wait(sd_bus *bus) { - int r; - - for (;;) { - r = sd_bus_process(bus, NULL); - if (r < 0) - return r; - if (r > 0) - return 0; - - r = sd_bus_wait(bus, (uint64_t) -1); - if (r < 0) - return r; - } -} - -static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { - _cleanup_free_ char *dbus_path = NULL; - - assert(d); - assert(d->name); - assert(result); - - if (!endswith(d->name, ".service")) - return -EINVAL; - - dbus_path = unit_dbus_path_from_name(d->name); - if (!dbus_path) - return -ENOMEM; - - return sd_bus_get_property_string(d->bus, - "org.freedesktop.systemd1", - dbus_path, - "org.freedesktop.systemd1.Service", - "Result", - NULL, - result); -} - -static const struct { - const char *result, *explanation; -} explanations [] = { - { "resources", "of unavailable resources or another system error" }, - { "protocol", "the service did not take the steps required by its unit configuration" }, - { "timeout", "a timeout was exceeded" }, - { "exit-code", "the control process exited with error code" }, - { "signal", "a fatal signal was delivered to the control process" }, - { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, - { "watchdog", "the service failed to send watchdog ping" }, - { "start-limit", "start of the service was attempted too often" } -}; - -static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { - _cleanup_free_ char *service_shell_quoted = NULL; - const char *systemctl = "systemctl", *journalctl = "journalctl"; - - assert(service); - - service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH); - - if (!strv_isempty((char**) extra_args)) { - _cleanup_free_ char *t; - - t = strv_join((char**) extra_args, " "); - systemctl = strjoina("systemctl ", t ? : ""); - journalctl = strjoina("journalctl ", t ? : ""); - } - - if (!isempty(result)) { - unsigned i; - - for (i = 0; i < ELEMENTSOF(explanations); ++i) - if (streq(result, explanations[i].result)) - break; - - if (i < ELEMENTSOF(explanations)) { - log_error("Job for %s failed because %s.\n" - "See \"%s status %s\" and \"%s -xe\" for details.\n", - service, - explanations[i].explanation, - systemctl, - service_shell_quoted ?: "", - journalctl); - goto finish; - } - } - - log_error("Job for %s failed.\n" - "See \"%s status %s\" and \"%s -xe\" for details.\n", - service, - systemctl, - service_shell_quoted ?: "", - journalctl); - -finish: - /* For some results maybe additional explanation is required */ - if (streq_ptr(result, "start-limit")) - log_info("To force a start use \"%1$s reset-failed %2$s\"\n" - "followed by \"%1$s start %2$s\" again.", - systemctl, - service_shell_quoted ?: ""); -} - -static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { - assert(d->result); - - if (!quiet) { - if (streq(d->result, "canceled")) - log_error("Job for %s canceled.", strna(d->name)); - else if (streq(d->result, "timeout")) - log_error("Job for %s timed out.", strna(d->name)); - else if (streq(d->result, "dependency")) - log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); - else if (streq(d->result, "invalid")) - log_error("%s is not active, cannot reload.", strna(d->name)); - else if (streq(d->result, "assert")) - log_error("Assertion failed on job for %s.", strna(d->name)); - else if (streq(d->result, "unsupported")) - log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); - else if (streq(d->result, "collected")) - log_error("Queued job for %s was garbage collected.", strna(d->name)); - else if (streq(d->result, "once")) - log_error("Unit %s was started already once and can't be started again.", strna(d->name)); - else if (!STR_IN_SET(d->result, "done", "skipped")) { - if (d->name) { - _cleanup_free_ char *result = NULL; - int q; - - q = bus_job_get_service_result(d, &result); - if (q < 0) - log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name); - - log_job_error_with_service_result(d->name, result, extra_args); - } else - log_error("Job failed. See \"journalctl -xe\" for details."); - } - } - - if (STR_IN_SET(d->result, "canceled", "collected")) - return -ECANCELED; - else if (streq(d->result, "timeout")) - return -ETIME; - else if (streq(d->result, "dependency")) - return -EIO; - else if (streq(d->result, "invalid")) - return -ENOEXEC; - else if (streq(d->result, "assert")) - return -EPROTO; - else if (streq(d->result, "unsupported")) - return -EOPNOTSUPP; - else if (streq(d->result, "once")) - return -ESTALE; - else if (STR_IN_SET(d->result, "done", "skipped")) - return 0; - - 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) { - int r = 0; - - assert(d); - - while (!set_isempty(d->jobs)) { - int q; - - q = bus_process_wait(d->bus); - if (q < 0) - return log_error_errno(q, "Failed to wait for response: %m"); - - if (d->result) { - q = check_wait_response(d, quiet, extra_args); - /* Return the first error as it is most likely to be - * meaningful. */ - if (q < 0 && r == 0) - r = q; - - log_debug_errno(q, "Got result %s/%m for job %s", strna(d->result), strna(d->name)); - } - - d->name = mfree(d->name); - d->result = mfree(d->result); - } - - return r; -} - -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { - int r; - - assert(d); - - r = set_ensure_allocated(&d->jobs, &string_hash_ops); - if (r < 0) - return r; - - return set_put_strdup(d->jobs, path); -} - -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { - int r; - - r = bus_wait_for_jobs_add(d, path); - if (r < 0) - return log_oom(); - - return bus_wait_for_jobs(d, quiet, NULL); -} - int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes) { const char *type, *path, *source; int r; @@ -2117,409 +1808,6 @@ int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, Un return 0; } -struct CGroupInfo { - char *cgroup_path; - bool is_const; /* If false, cgroup_path should be free()'d */ - - Hashmap *pids; /* PID → process name */ - bool done; - - struct CGroupInfo *parent; - LIST_FIELDS(struct CGroupInfo, siblings); - LIST_HEAD(struct CGroupInfo, children); - size_t n_children; -}; - -static bool IS_ROOT(const char *p) { - return isempty(p) || streq(p, "/"); -} - -static int add_cgroup(Hashmap *cgroups, const char *path, bool is_const, struct CGroupInfo **ret) { - struct CGroupInfo *parent = NULL, *cg; - int r; - - assert(cgroups); - assert(ret); - - if (IS_ROOT(path)) - path = "/"; - - cg = hashmap_get(cgroups, path); - if (cg) { - *ret = cg; - return 0; - } - - if (!IS_ROOT(path)) { - const char *e, *pp; - - e = strrchr(path, '/'); - if (!e) - return -EINVAL; - - pp = strndupa(path, e - path); - if (!pp) - return -ENOMEM; - - r = add_cgroup(cgroups, pp, false, &parent); - if (r < 0) - return r; - } - - cg = new0(struct CGroupInfo, 1); - if (!cg) - return -ENOMEM; - - if (is_const) - cg->cgroup_path = (char*) path; - else { - cg->cgroup_path = strdup(path); - if (!cg->cgroup_path) { - free(cg); - return -ENOMEM; - } - } - - cg->is_const = is_const; - cg->parent = parent; - - r = hashmap_put(cgroups, cg->cgroup_path, cg); - if (r < 0) { - if (!is_const) - free(cg->cgroup_path); - free(cg); - return r; - } - - if (parent) { - LIST_PREPEND(siblings, parent->children, cg); - parent->n_children++; - } - - *ret = cg; - return 1; -} - -static int add_process( - Hashmap *cgroups, - const char *path, - pid_t pid, - const char *name) { - - struct CGroupInfo *cg; - int r; - - assert(cgroups); - assert(name); - assert(pid > 0); - - r = add_cgroup(cgroups, path, true, &cg); - if (r < 0) - return r; - - r = hashmap_ensure_allocated(&cg->pids, &trivial_hash_ops); - if (r < 0) - return r; - - return hashmap_put(cg->pids, PID_TO_PTR(pid), (void*) name); -} - -static void remove_cgroup(Hashmap *cgroups, struct CGroupInfo *cg) { - assert(cgroups); - assert(cg); - - while (cg->children) - remove_cgroup(cgroups, cg->children); - - hashmap_remove(cgroups, cg->cgroup_path); - - if (!cg->is_const) - free(cg->cgroup_path); - - hashmap_free(cg->pids); - - if (cg->parent) - LIST_REMOVE(siblings, cg->parent->children, cg); - - free(cg); -} - -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( - Hashmap *cgroups, - const char *cgroup_path, - const char *prefix, - unsigned n_columns, - OutputFlags flags) { - - struct CGroupInfo *cg; - int r; - - assert(prefix); - - if (IS_ROOT(cgroup_path)) - cgroup_path = "/"; - - cg = hashmap_get(cgroups, cgroup_path); - if (!cg) - return 0; - - if (!hashmap_isempty(cg->pids)) { - const char *name; - size_t n = 0, i; - pid_t *pids; - void *pidp; - Iterator j; - int width; - - /* Order processes by their PID */ - pids = newa(pid_t, hashmap_size(cg->pids)); - - HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) - pids[n++] = PTR_TO_PID(pidp); - - assert(n == hashmap_size(cg->pids)); - typesafe_qsort(pids, n, pid_compare_func); - - width = DECIMAL_STR_WIDTH(pids[n-1]); - - for (i = 0; i < n; i++) { - _cleanup_free_ char *e = NULL; - const char *special; - bool more; - - name = hashmap_get(cg->pids, PID_TO_PTR(pids[i])); - assert(name); - - if (n_columns != 0) { - unsigned k; - - k = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); - - e = ellipsize(name, k, 100); - if (e) - name = e; - } - - more = i+1 < n || cg->children; - special = special_glyph(more ? SPECIAL_GLYPH_TREE_BRANCH : SPECIAL_GLYPH_TREE_RIGHT); - - fprintf(stdout, "%s%s%*"PID_PRI" %s\n", - prefix, - special, - width, pids[i], - name); - } - } - - if (cg->children) { - struct CGroupInfo **children, *child; - size_t n = 0, i; - - /* Order subcgroups by their name */ - children = newa(struct CGroupInfo*, cg->n_children); - LIST_FOREACH(siblings, child, cg->children) - children[n++] = child; - assert(n == cg->n_children); - typesafe_qsort(children, n, cgroup_info_compare_func); - - if (n_columns != 0) - n_columns = MAX(LESS_BY(n_columns, 2U), 20U); - - for (i = 0; i < n; i++) { - _cleanup_free_ char *pp = NULL; - const char *name, *special; - bool more; - - child = children[i]; - - name = strrchr(child->cgroup_path, '/'); - if (!name) - return -EINVAL; - name++; - - more = i+1 < n; - 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 ? SPECIAL_GLYPH_TREE_VERTICAL : SPECIAL_GLYPH_TREE_SPACE); - - pp = strappend(prefix, special); - if (!pp) - return -ENOMEM; - - r = dump_processes(cgroups, child->cgroup_path, pp, n_columns, flags); - if (r < 0) - return r; - } - } - - cg->done = true; - return 0; -} - -static int dump_extra_processes( - Hashmap *cgroups, - const char *prefix, - unsigned n_columns, - OutputFlags flags) { - - _cleanup_free_ pid_t *pids = NULL; - _cleanup_hashmap_free_ Hashmap *names = NULL; - struct CGroupInfo *cg; - size_t n_allocated = 0, n = 0, k; - Iterator i; - int width, r; - - /* Prints the extra processes, i.e. those that are in cgroups we haven't displayed yet. We show them as - * combined, sorted, linear list. */ - - HASHMAP_FOREACH(cg, cgroups, i) { - const char *name; - void *pidp; - Iterator j; - - if (cg->done) - continue; - - if (hashmap_isempty(cg->pids)) - continue; - - r = hashmap_ensure_allocated(&names, &trivial_hash_ops); - if (r < 0) - return r; - - if (!GREEDY_REALLOC(pids, n_allocated, n + hashmap_size(cg->pids))) - return -ENOMEM; - - HASHMAP_FOREACH_KEY(name, pidp, cg->pids, j) { - pids[n++] = PTR_TO_PID(pidp); - - r = hashmap_put(names, pidp, (void*) name); - if (r < 0) - return r; - } - } - - if (n == 0) - return 0; - - typesafe_qsort(pids, n, pid_compare_func); - width = DECIMAL_STR_WIDTH(pids[n-1]); - - for (k = 0; k < n; k++) { - _cleanup_free_ char *e = NULL; - const char *name; - - name = hashmap_get(names, PID_TO_PTR(pids[k])); - assert(name); - - if (n_columns != 0) { - unsigned z; - - z = MAX(LESS_BY(n_columns, 2U + width + 1U), 20U); - - e = ellipsize(name, z, 100); - if (e) - name = e; - } - - fprintf(stdout, "%s%s %*" PID_PRI " %s\n", - prefix, - special_glyph(SPECIAL_GLYPH_TRIANGULAR_BULLET), - width, pids[k], - name); - } - - return 0; -} - -int unit_show_processes( - sd_bus *bus, - const char *unit, - const char *cgroup_path, - const char *prefix, - unsigned n_columns, - OutputFlags flags, - sd_bus_error *error) { - - _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; - Hashmap *cgroups = NULL; - struct CGroupInfo *cg; - int r; - - assert(bus); - assert(unit); - - if (flags & OUTPUT_FULL_WIDTH) - n_columns = 0; - else if (n_columns <= 0) - n_columns = columns(); - - prefix = strempty(prefix); - - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "GetUnitProcesses", - error, - &reply, - "s", - unit); - if (r < 0) - return r; - - cgroups = hashmap_new(&path_hash_ops); - if (!cgroups) - return -ENOMEM; - - r = sd_bus_message_enter_container(reply, 'a', "(sus)"); - if (r < 0) - goto finish; - - for (;;) { - const char *path = NULL, *name = NULL; - uint32_t pid; - - r = sd_bus_message_read(reply, "(sus)", &path, &pid, &name); - if (r < 0) - goto finish; - if (r == 0) - break; - - r = add_process(cgroups, path, pid, name); - if (r < 0) - goto finish; - } - - r = sd_bus_message_exit_container(reply); - if (r < 0) - goto finish; - - r = dump_processes(cgroups, cgroup_path, prefix, n_columns, flags); - if (r < 0) - goto finish; - - r = dump_extra_processes(cgroups, prefix, n_columns, flags); - -finish: - while ((cg = hashmap_first(cgroups))) - remove_cgroup(cgroups, cg); - - hashmap_free(cgroups); - - return r; -} - int unit_load_state(sd_bus *bus, const char *name, char **load_state) { _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; _cleanup_free_ char *path = NULL; diff --git a/src/shared/bus-unit-util.h b/src/shared/bus-unit-util.h index 4fc94b0f4..a0b496f62 100644 --- a/src/shared/bus-unit-util.h +++ b/src/shared/bus-unit-util.h @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -#include "install.h" -#include "output-mode.h" #include "sd-bus.h" + +#include "install.h" #include "unit-def.h" typedef struct UnitInfo { @@ -25,18 +25,6 @@ int bus_parse_unit_info(sd_bus_message *message, UnitInfo *u); int bus_append_unit_property_assignment(sd_bus_message *m, UnitType t, const char *assignment); int bus_append_unit_property_assignment_many(sd_bus_message *m, UnitType t, char **l); -typedef struct BusWaitForJobs BusWaitForJobs; - -int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); -void bus_wait_for_jobs_free(BusWaitForJobs *d); -int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); -int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); -int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); - -DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); - int bus_deserialize_and_dump_unit_file_changes(sd_bus_message *m, bool quiet, UnitFileChange **changes, size_t *n_changes); -int unit_show_processes(sd_bus *bus, const char *unit, const char *cgroup_path, const char *prefix, unsigned n_columns, OutputFlags flags, sd_bus_error *error); - int unit_load_state(sd_bus *bus, const char *name, char **load_state); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index cbcf698e9..e31fcfbd5 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -410,14 +410,14 @@ int bus_verify_polkit_async( if (sd_bus_message_is_method_error(q->reply, NULL)) { const sd_bus_error *e; - /* Copy error from polkit reply */ e = sd_bus_message_get_error(q->reply); - sd_bus_error_copy(error, e); /* Treat no PK available as access denied */ if (sd_bus_error_has_name(e, SD_BUS_ERROR_SERVICE_UNKNOWN)) return -EACCES; + /* Copy error from polkit reply */ + sd_bus_error_copy(error, e); return -sd_bus_error_get_errno(e); } @@ -628,7 +628,21 @@ int bus_connect_user_systemd(sd_bus **_bus) { return 0; } -int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) { +int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value) { + assert(name); + + if (expected_value && !streq_ptr(expected_value, value)) + return 0; + + if (only_value) + puts(value); + else + printf("%s=%s\n", name, value); + + return 0; +} + +int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) { va_list ap; int r; @@ -691,7 +705,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b /* This property has a single value, so we need to take * care not to print a new line, everything else is OK. */ good = !strchr(s, '\n'); - bus_print_property_value(name, expected_value, value, "%s", good ? s : "[unprintable]"); + bus_print_property_value(name, expected_value, value, good ? s : "[unprintable]"); } return 1; @@ -707,7 +721,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if (expected_value && parse_boolean(expected_value) != b) return 1; - bus_print_property_value(name, NULL, value, "%s", yes_no(b)); + bus_print_property_value(name, NULL, value, yes_no(b)); return 1; } @@ -728,13 +742,13 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b t = format_timestamp(timestamp, sizeof(timestamp), u); if (t || all) - bus_print_property_value(name, expected_value, value, "%s", strempty(t)); + bus_print_property_value(name, expected_value, value, strempty(t)); } else if (strstr(name, "USec")) { char timespan[FORMAT_TIMESPAN_MAX]; (void) format_timespan(timespan, sizeof(timespan), u, 0); - bus_print_property_value(name, expected_value, value, "%s", timespan); + bus_print_property_value(name, expected_value, value, timespan); } else if (streq(name, "RestrictNamespaces")) { _cleanup_free_ char *s = NULL; @@ -742,7 +756,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if ((u & NAMESPACE_FLAGS_ALL) == 0) result = "yes"; - else if ((u & NAMESPACE_FLAGS_ALL) == NAMESPACE_FLAGS_ALL) + else if (FLAGS_SET(u, NAMESPACE_FLAGS_ALL)) result = "no"; else { r = namespace_flags_to_string(u, &s); @@ -752,7 +766,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b result = s; } - bus_print_property_value(name, expected_value, value, "%s", result); + bus_print_property_value(name, expected_value, value, result); } else if (streq(name, "MountFlags")) { const char *result; @@ -761,7 +775,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if (!result) return -EINVAL; - bus_print_property_value(name, expected_value, value, "%s", result); + bus_print_property_value(name, expected_value, value, result); } else if (STR_IN_SET(name, "CapabilityBoundingSet", "AmbientCapabilities")) { _cleanup_free_ char *s = NULL; @@ -770,7 +784,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if (r < 0) return r; - bus_print_property_value(name, expected_value, value, "%s", s); + bus_print_property_value(name, expected_value, value, s); } else if ((STR_IN_SET(name, "CPUWeight", "StartupCPUWeight", "IOWeight", "StartupIOWeight") && u == CGROUP_WEIGHT_INVALID) || (STR_IN_SET(name, "CPUShares", "StartupCPUShares") && u == CGROUP_CPU_SHARES_INVALID) || @@ -778,16 +792,18 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b (STR_IN_SET(name, "MemoryCurrent", "TasksCurrent") && u == (uint64_t) -1) || (endswith(name, "NSec") && u == (uint64_t) -1)) - bus_print_property_value(name, expected_value, value, "%s", "[not set]"); + bus_print_property_value(name, expected_value, value, "[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)) - bus_print_property_value(name, expected_value, value, "%s", "infinity"); + bus_print_property_value(name, expected_value, value, "infinity"); + else if (STR_IN_SET(name, "IPIngressBytes", "IPIngressPackets", "IPEgressBytes", "IPEgressPackets") && u == (uint64_t) -1) + bus_print_property_value(name, expected_value, value, "[no data]"); else - bus_print_property_value(name, expected_value, value, "%"PRIu64, u); + bus_print_property_valuef(name, expected_value, value, "%"PRIu64, u); return 1; } @@ -799,7 +815,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if (r < 0) return r; - bus_print_property_value(name, expected_value, value, "%"PRIi64, i); + bus_print_property_valuef(name, expected_value, value, "%"PRIi64, i); return 1; } @@ -811,20 +827,20 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b return r; if (strstr(name, "UMask") || strstr(name, "Mode")) - bus_print_property_value(name, expected_value, value, "%04o", u); + bus_print_property_valuef(name, expected_value, value, "%04o", u); else if (streq(name, "UID")) { if (u == UID_INVALID) - bus_print_property_value(name, expected_value, value, "%s", "[not set]"); + bus_print_property_value(name, expected_value, value, "[not set]"); else - bus_print_property_value(name, expected_value, value, "%"PRIu32, u); + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); } else if (streq(name, "GID")) { if (u == GID_INVALID) - bus_print_property_value(name, expected_value, value, "%s", "[not set]"); + bus_print_property_value(name, expected_value, value, "[not set]"); else - bus_print_property_value(name, expected_value, value, "%"PRIu32, u); + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); } else - bus_print_property_value(name, expected_value, value, "%"PRIu32, u); + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); return 1; } @@ -836,7 +852,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if (r < 0) return r; - bus_print_property_value(name, expected_value, value, "%"PRIi32, i); + bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i); return 1; } @@ -847,7 +863,7 @@ static int bus_print_property(const char *name, const char *expected_value, sd_b if (r < 0) return r; - bus_print_property_value(name, expected_value, value, "%g", d); + bus_print_property_valuef(name, expected_value, value, "%g", d); return 1; } diff --git a/src/shared/bus-util.h b/src/shared/bus-util.h index 71c248fe3..59bfdb239 100644 --- a/src/shared/bus-util.h +++ b/src/shared/bus-util.h @@ -12,6 +12,7 @@ #include "hashmap.h" #include "macro.h" #include "string-util.h" +#include "time-util.h" typedef enum BusTransport { BUS_TRANSPORT_LOCAL, @@ -64,7 +65,8 @@ int bus_connect_transport_systemd(BusTransport transport, const char *host, bool typedef int (*bus_message_print_t) (const char *name, const char *expected_value, sd_bus_message *m, bool value, bool all); -int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5); +int bus_print_property_value(const char *name, const char *expected_value, bool only_value, const char *value); +int bus_print_property_valuef(const char *name, const char *expected_value, bool only_value, const char *fmt, ...) _printf_(4,5); int bus_message_print_all_properties(sd_bus_message *m, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties); int bus_print_all_properties(sd_bus *bus, const char *dest, const char *path, bus_message_print_t func, char **filter, bool value, bool all, Set **found_properties); diff --git a/src/shared/bus-wait-for-jobs.c b/src/shared/bus-wait-for-jobs.c new file mode 100644 index 000000000..4e6b862d5 --- /dev/null +++ b/src/shared/bus-wait-for-jobs.c @@ -0,0 +1,337 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "bus-wait-for-jobs.h" +#include "set.h" +#include "bus-util.h" +#include "bus-internal.h" +#include "unit-def.h" +#include "escape.h" +#include "strv.h" + +typedef struct BusWaitForJobs { + sd_bus *bus; + + /* The set of jobs to wait for, as bus object paths */ + Set *jobs; + + /* The unit name and job result of the last Job message */ + char *name; + char *result; + + sd_bus_slot *slot_job_removed; + sd_bus_slot *slot_disconnected; +} BusWaitForJobs; + +static int match_disconnected(sd_bus_message *m, void *userdata, sd_bus_error *error) { + assert(m); + + log_error("Warning! D-Bus connection terminated."); + sd_bus_close(sd_bus_message_get_bus(m)); + + return 0; +} + +static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *error) { + const char *path, *unit, *result; + BusWaitForJobs *d = userdata; + uint32_t id; + char *found; + int r; + + assert(m); + assert(d); + + r = sd_bus_message_read(m, "uoss", &id, &path, &unit, &result); + if (r < 0) { + bus_log_parse_error(r); + return 0; + } + + found = set_remove(d->jobs, (char*) path); + if (!found) + return 0; + + free(found); + + (void) free_and_strdup(&d->result, empty_to_null(result)); + + (void) free_and_strdup(&d->name, empty_to_null(unit)); + + return 0; +} + +void bus_wait_for_jobs_free(BusWaitForJobs *d) { + if (!d) + return; + + set_free_free(d->jobs); + + sd_bus_slot_unref(d->slot_disconnected); + sd_bus_slot_unref(d->slot_job_removed); + + sd_bus_unref(d->bus); + + free(d->name); + free(d->result); + + free(d); +} + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret) { + _cleanup_(bus_wait_for_jobs_freep) BusWaitForJobs *d = NULL; + int r; + + assert(bus); + assert(ret); + + d = new(BusWaitForJobs, 1); + if (!d) + return -ENOMEM; + + *d = (BusWaitForJobs) { + .bus = sd_bus_ref(bus), + }; + + /* When we are a bus client we match by sender. Direct + * connections OTOH have no initialized sender field, and + * hence we ignore the sender then */ + r = sd_bus_match_signal_async( + bus, + &d->slot_job_removed, + bus->bus_client ? "org.freedesktop.systemd1" : NULL, + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "JobRemoved", + match_job_removed, NULL, d); + if (r < 0) + return r; + + r = sd_bus_match_signal_async( + bus, + &d->slot_disconnected, + "org.freedesktop.DBus.Local", + NULL, + "org.freedesktop.DBus.Local", + "Disconnected", + match_disconnected, NULL, d); + if (r < 0) + return r; + + *ret = TAKE_PTR(d); + + return 0; +} + +static int bus_process_wait(sd_bus *bus) { + int r; + + for (;;) { + r = sd_bus_process(bus, NULL); + if (r < 0) + return r; + if (r > 0) + return 0; + + r = sd_bus_wait(bus, (uint64_t) -1); + if (r < 0) + return r; + } +} + +static int bus_job_get_service_result(BusWaitForJobs *d, char **result) { + _cleanup_free_ char *dbus_path = NULL; + + assert(d); + assert(d->name); + assert(result); + + if (!endswith(d->name, ".service")) + return -EINVAL; + + dbus_path = unit_dbus_path_from_name(d->name); + if (!dbus_path) + return -ENOMEM; + + return sd_bus_get_property_string(d->bus, + "org.freedesktop.systemd1", + dbus_path, + "org.freedesktop.systemd1.Service", + "Result", + NULL, + result); +} + +static void log_job_error_with_service_result(const char* service, const char *result, const char* const* extra_args) { + _cleanup_free_ char *service_shell_quoted = NULL; + const char *systemctl = "systemctl", *journalctl = "journalctl"; + + static const struct { + const char *result, *explanation; + } explanations[] = { + { "resources", "of unavailable resources or another system error" }, + { "protocol", "the service did not take the steps required by its unit configuration" }, + { "timeout", "a timeout was exceeded" }, + { "exit-code", "the control process exited with error code" }, + { "signal", "a fatal signal was delivered to the control process" }, + { "core-dump", "a fatal signal was delivered causing the control process to dump core" }, + { "watchdog", "the service failed to send watchdog ping" }, + { "start-limit", "start of the service was attempted too often" } + }; + + assert(service); + + service_shell_quoted = shell_maybe_quote(service, ESCAPE_BACKSLASH); + + if (!strv_isempty((char**) extra_args)) { + _cleanup_free_ char *t; + + t = strv_join((char**) extra_args, " "); + systemctl = strjoina("systemctl ", t ? : ""); + journalctl = strjoina("journalctl ", t ? : ""); + } + + if (!isempty(result)) { + size_t i; + + for (i = 0; i < ELEMENTSOF(explanations); ++i) + if (streq(result, explanations[i].result)) + break; + + if (i < ELEMENTSOF(explanations)) { + log_error("Job for %s failed because %s.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + explanations[i].explanation, + systemctl, + service_shell_quoted ?: "", + journalctl); + goto finish; + } + } + + log_error("Job for %s failed.\n" + "See \"%s status %s\" and \"%s -xe\" for details.\n", + service, + systemctl, + service_shell_quoted ?: "", + journalctl); + +finish: + /* For some results maybe additional explanation is required */ + if (streq_ptr(result, "start-limit")) + log_info("To force a start use \"%1$s reset-failed %2$s\"\n" + "followed by \"%1$s start %2$s\" again.", + systemctl, + service_shell_quoted ?: ""); +} + +static int check_wait_response(BusWaitForJobs *d, bool quiet, const char* const* extra_args) { + assert(d); + assert(d->name); + assert(d->result); + + if (!quiet) { + if (streq(d->result, "canceled")) + log_error("Job for %s canceled.", strna(d->name)); + else if (streq(d->result, "timeout")) + log_error("Job for %s timed out.", strna(d->name)); + else if (streq(d->result, "dependency")) + log_error("A dependency job for %s failed. See 'journalctl -xe' for details.", strna(d->name)); + else if (streq(d->result, "invalid")) + log_error("%s is not active, cannot reload.", strna(d->name)); + else if (streq(d->result, "assert")) + log_error("Assertion failed on job for %s.", strna(d->name)); + else if (streq(d->result, "unsupported")) + log_error("Operation on or unit type of %s not supported on this system.", strna(d->name)); + else if (streq(d->result, "collected")) + log_error("Queued job for %s was garbage collected.", strna(d->name)); + else if (streq(d->result, "once")) + log_error("Unit %s was started already once and can't be started again.", strna(d->name)); + else if (!STR_IN_SET(d->result, "done", "skipped")) { + + if (d->name && endswith(d->name, ".service")) { + _cleanup_free_ char *result = NULL; + int q; + + q = bus_job_get_service_result(d, &result); + if (q < 0) + log_debug_errno(q, "Failed to get Result property of unit %s: %m", d->name); + + log_job_error_with_service_result(d->name, result, extra_args); + } else + log_error("Job failed. See \"journalctl -xe\" for details."); + } + } + + if (STR_IN_SET(d->result, "canceled", "collected")) + return -ECANCELED; + else if (streq(d->result, "timeout")) + return -ETIME; + else if (streq(d->result, "dependency")) + return -EIO; + else if (streq(d->result, "invalid")) + return -ENOEXEC; + else if (streq(d->result, "assert")) + return -EPROTO; + else if (streq(d->result, "unsupported")) + return -EOPNOTSUPP; + else if (streq(d->result, "once")) + return -ESTALE; + else if (STR_IN_SET(d->result, "done", "skipped")) + return 0; + + 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) { + int r = 0; + + assert(d); + + while (!set_isempty(d->jobs)) { + int q; + + q = bus_process_wait(d->bus); + if (q < 0) + return log_error_errno(q, "Failed to wait for response: %m"); + + if (d->name && d->result) { + q = check_wait_response(d, quiet, extra_args); + /* Return the first error as it is most likely to be + * meaningful. */ + if (q < 0 && r == 0) + r = q; + + log_debug_errno(q, "Got result %s/%m for job %s", d->result, d->name); + } + + d->name = mfree(d->name); + d->result = mfree(d->result); + } + + return r; +} + +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path) { + int r; + + assert(d); + + r = set_ensure_allocated(&d->jobs, &string_hash_ops); + if (r < 0) + return r; + + return set_put_strdup(d->jobs, path); +} + +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet) { + int r; + + r = bus_wait_for_jobs_add(d, path); + if (r < 0) + return log_oom(); + + return bus_wait_for_jobs(d, quiet, NULL); +} diff --git a/src/shared/bus-wait-for-jobs.h b/src/shared/bus-wait-for-jobs.h new file mode 100644 index 000000000..8f21eb9cd --- /dev/null +++ b/src/shared/bus-wait-for-jobs.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "sd-bus.h" +#include "macro.h" + +typedef struct BusWaitForJobs BusWaitForJobs; + +int bus_wait_for_jobs_new(sd_bus *bus, BusWaitForJobs **ret); +void bus_wait_for_jobs_free(BusWaitForJobs *d); +int bus_wait_for_jobs_add(BusWaitForJobs *d, const char *path); +int bus_wait_for_jobs(BusWaitForJobs *d, bool quiet, const char* const* extra_args); +int bus_wait_for_jobs_one(BusWaitForJobs *d, const char *path, bool quiet); + +DEFINE_TRIVIAL_CLEANUP_FUNC(BusWaitForJobs*, bus_wait_for_jobs_free); diff --git a/src/shared/calendarspec.c b/src/shared/calendarspec.c index dafc09e8f..d83e7962a 100644 --- a/src/shared/calendarspec.c +++ b/src/shared/calendarspec.c @@ -14,10 +14,12 @@ #include "alloc-util.h" #include "calendarspec.h" +#include "errno-util.h" #include "fileio.h" #include "macro.h" #include "parse-util.h" #include "process-util.h" +#include "sort-util.h" #include "string-util.h" #include "time-util.h" @@ -31,7 +33,7 @@ * linked compenents anyway. */ #define CALENDARSPEC_COMPONENTS_MAX 240 -static void free_chain(CalendarComponent *c) { +static void chain_free(CalendarComponent *c) { CalendarComponent *n; while (c) { @@ -41,17 +43,19 @@ static void free_chain(CalendarComponent *c) { } } +DEFINE_TRIVIAL_CLEANUP_FUNC(CalendarComponent*, chain_free); + CalendarSpec* calendar_spec_free(CalendarSpec *c) { if (!c) return NULL; - free_chain(c->year); - free_chain(c->month); - free_chain(c->day); - free_chain(c->hour); - free_chain(c->minute); - free_chain(c->microsecond); + chain_free(c->year); + chain_free(c->month); + chain_free(c->day); + chain_free(c->hour); + chain_free(c->minute); + chain_free(c->microsecond); free(c->timezone); return mfree(c); @@ -549,14 +553,16 @@ static int const_chain(int value, CalendarComponent **c) { assert(c); - cc = new0(CalendarComponent, 1); + cc = new(CalendarComponent, 1); if (!cc) return -ENOMEM; - cc->start = value; - cc->stop = -1; - cc->repeat = 0; - cc->next = *c; + *cc = (CalendarComponent) { + .start = value, + .stop = -1, + .repeat = 0, + .next = *c, + }; *c = cc; @@ -564,13 +570,18 @@ static int const_chain(int value, CalendarComponent **c) { } static int calendarspec_from_time_t(CalendarSpec *c, time_t time) { + _cleanup_(chain_freep) CalendarComponent + *year = NULL, *month = NULL, *day = NULL, + *hour = NULL, *minute = NULL, *us = NULL; struct tm tm; - CalendarComponent *year = NULL, *month = NULL, *day = NULL, *hour = NULL, *minute = NULL, *us = NULL; int r; if (!gmtime_r(&time, &tm)) return -ERANGE; + if (tm.tm_year > INT_MAX - 1900) + return -ERANGE; + r = const_chain(tm.tm_year + 1900, &year); if (r < 0) return r; @@ -596,12 +607,12 @@ static int calendarspec_from_time_t(CalendarSpec *c, time_t time) { return r; c->utc = true; - c->year = year; - c->month = month; - c->day = day; - c->hour = hour; - c->minute = minute; - c->microsecond = us; + c->year = TAKE_PTR(year); + c->month = TAKE_PTR(month); + c->day = TAKE_PTR(day); + c->hour = TAKE_PTR(hour); + c->minute = TAKE_PTR(minute); + c->microsecond = TAKE_PTR(us); return 0; } @@ -642,14 +653,16 @@ static int prepend_component(const char **p, bool usec, unsigned nesting, Calend if (!IN_SET(*e, 0, ' ', ',', '-', '~', ':')) return -EINVAL; - cc = new0(CalendarComponent, 1); + cc = new(CalendarComponent, 1); if (!cc) return -ENOMEM; - cc->start = start; - cc->stop = stop; - cc->repeat = repeat; - cc->next = *c; + *cc = (CalendarComponent) { + .start = start, + .stop = stop, + .repeat = repeat, + .next = *c, + }; *p = e; *c = cc; @@ -663,8 +676,8 @@ static int prepend_component(const char **p, bool usec, unsigned nesting, Calend } static int parse_chain(const char **p, bool usec, CalendarComponent **c) { + _cleanup_(chain_freep) CalendarComponent *cc = NULL; const char *t; - CalendarComponent *cc = NULL; int r; assert(p); @@ -686,20 +699,18 @@ static int parse_chain(const char **p, bool usec, CalendarComponent **c) { } r = prepend_component(&t, usec, 0, &cc); - if (r < 0) { - free_chain(cc); + if (r < 0) return r; - } *p = t; - *c = cc; + *c = TAKE_PTR(cc); return 0; } static int parse_date(const char **p, CalendarSpec *c) { + _cleanup_(chain_freep) CalendarComponent *first = NULL, *second = NULL, *third = NULL; const char *t; int r; - CalendarComponent *first, *second, *third; assert(p); assert(*p); @@ -736,70 +747,51 @@ static int parse_date(const char **p, CalendarSpec *c) { return r; /* Already the end? A ':' as separator? In that case this was a time, not a date */ - if (IN_SET(*t, 0, ':')) { - free_chain(first); + if (IN_SET(*t, 0, ':')) return 0; - } if (*t == '~') c->end_of_month = true; - else if (*t != '-') { - free_chain(first); + else if (*t != '-') return -EINVAL; - } t++; r = parse_chain(&t, false, &second); - if (r < 0) { - free_chain(first); + if (r < 0) return r; - } /* Got two parts, hence it's month and day */ if (IN_SET(*t, 0, ' ')) { *p = t + strspn(t, " "); - c->month = first; - c->day = second; + c->month = TAKE_PTR(first); + c->day = TAKE_PTR(second); return 0; - } else if (c->end_of_month) { - free_chain(first); - free_chain(second); + } else if (c->end_of_month) return -EINVAL; - } if (*t == '~') c->end_of_month = true; - else if (*t != '-') { - free_chain(first); - free_chain(second); + else if (*t != '-') return -EINVAL; - } t++; r = parse_chain(&t, false, &third); - if (r < 0) { - free_chain(first); - free_chain(second); + if (r < 0) return r; - } + + if (!IN_SET(*t, 0, ' ')) + return -EINVAL; /* Got three parts, hence it is year, month and day */ - if (IN_SET(*t, 0, ' ')) { - *p = t + strspn(t, " "); - c->year = first; - c->month = second; - c->day = third; - return 0; - } - - free_chain(first); - free_chain(second); - free_chain(third); - return -EINVAL; + *p = t + strspn(t, " "); + c->year = TAKE_PTR(first); + c->month = TAKE_PTR(second); + c->day = TAKE_PTR(third); + return 0; } static int parse_calendar_time(const char **p, CalendarSpec *c) { - CalendarComponent *h = NULL, *m = NULL, *s = NULL; + _cleanup_(chain_freep) CalendarComponent *h = NULL, *m = NULL, *s = NULL; const char *t; int r; @@ -815,86 +807,81 @@ static int parse_calendar_time(const char **p, CalendarSpec *c) { r = parse_chain(&t, false, &h); if (r < 0) - goto fail; + return r; - if (*t != ':') { - r = -EINVAL; - goto fail; - } + if (*t != ':') + return -EINVAL; t++; r = parse_chain(&t, false, &m); if (r < 0) - goto fail; + return r; /* Already at the end? Then it's hours and minutes, and seconds are 0 */ if (*t == 0) goto null_second; - if (*t != ':') { - r = -EINVAL; - goto fail; - } + if (*t != ':') + return -EINVAL; t++; r = parse_chain(&t, true, &s); if (r < 0) - goto fail; + return r; /* At the end? Then it's hours, minutes and seconds */ if (*t == 0) goto finish; - r = -EINVAL; - goto fail; + return -EINVAL; null_hour: r = const_chain(0, &h); if (r < 0) - goto fail; + return r; r = const_chain(0, &m); if (r < 0) - goto fail; + return r; null_second: r = const_chain(0, &s); if (r < 0) - goto fail; + return r; finish: *p = t; - c->hour = h; - c->minute = m; - c->microsecond = s; + c->hour = TAKE_PTR(h); + c->minute = TAKE_PTR(m); + c->microsecond = TAKE_PTR(s); return 0; - -fail: - free_chain(h); - free_chain(m); - free_chain(s); - return r; } int calendar_spec_from_string(const char *p, CalendarSpec **spec) { const char *utc; _cleanup_(calendar_spec_freep) CalendarSpec *c = NULL; + _cleanup_free_ char *p_tmp = NULL; int r; assert(p); assert(spec); - c = new0(CalendarSpec, 1); + c = new(CalendarSpec, 1); if (!c) return -ENOMEM; - c->dst = -1; - c->timezone = NULL; + + *c = (CalendarSpec) { + .dst = -1, + .timezone = NULL, + }; utc = endswith_no_case(p, " UTC"); if (utc) { c->utc = true; - p = strndupa(p, utc - p); + p = p_tmp = strndup(p, utc - p); + if (!p) + return -ENOMEM; } else { const char *e = NULL; int j; @@ -919,7 +906,10 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { /* Found one of the two timezones specified? */ if (IN_SET(j, 0, 1)) { - p = strndupa(p, e - p - 1); + p = p_tmp = strndup(p, e - p - 1); + if (!p) + return -ENOMEM; + c->dst = j; } else { const char *last_space; @@ -930,7 +920,9 @@ int calendar_spec_from_string(const char *p, CalendarSpec **spec) { if (!c->timezone) return -ENOMEM; - p = strndupa(p, last_space - p); + p = p_tmp = strndup(p, last_space - p); + if (!p) + return -ENOMEM; } } } diff --git a/src/shared/cgroup-show.c b/src/shared/cgroup-show.c index 61df7511a..91a243944 100644 --- a/src/shared/cgroup-show.c +++ b/src/shared/cgroup-show.c @@ -20,6 +20,7 @@ #include "output-mode.h" #include "path-util.h" #include "process-util.h" +#include "sort-util.h" #include "string-util.h" #include "terminal-util.h" #include "unit-name.h" diff --git a/src/shared/clock-util.c b/src/shared/clock-util.c index 1877a8143..32cce1e10 100644 --- a/src/shared/clock-util.c +++ b/src/shared/clock-util.c @@ -12,11 +12,11 @@ #include "alloc-util.h" #include "clock-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "macro.h" #include "string-util.h" -#include "util.h" int clock_get_hwclock(struct tm *tm) { _cleanup_close_ int fd = -1; diff --git a/src/shared/condition.c b/src/shared/condition.c index fb7796626..d1ac9de75 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -22,16 +22,17 @@ #include "cgroup-util.h" #include "condition.h" #include "efivars.h" +#include "env-file.h" #include "extract-word.h" #include "fd-util.h" #include "fileio.h" #include "glob-util.h" #include "hostname-util.h" #include "ima-util.h" +#include "limits-util.h" #include "list.h" #include "macro.h" #include "mountpoint-util.h" -#include "env-file.h" #include "parse-util.h" #include "path-util.h" #include "proc-cmdline.h" @@ -48,23 +49,25 @@ Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { Condition *c; - int r; assert(type >= 0); assert(type < _CONDITION_TYPE_MAX); assert((!parameter) == (type == CONDITION_NULL)); - c = new0(Condition, 1); + c = new(Condition, 1); if (!c) return NULL; - c->type = type; - c->trigger = trigger; - c->negate = negate; + *c = (Condition) { + .type = type, + .trigger = trigger, + .negate = negate, + }; - r = free_and_strdup(&c->parameter, parameter); - if (r < 0) { - return mfree(c); + if (parameter) { + c->parameter = strdup(parameter); + if (!c->parameter) + return mfree(c); } return c; @@ -77,13 +80,17 @@ void condition_free(Condition *c) { free(c); } -Condition* condition_free_list(Condition *first) { +Condition* condition_free_list_type(Condition *head, ConditionType type) { Condition *c, *n; - LIST_FOREACH_SAFE(conditions, c, n, first) - condition_free(c); + LIST_FOREACH_SAFE(conditions, c, n, head) + if (type < 0 || c->type == type) { + LIST_REMOVE(conditions, head, c); + condition_free(c); + } - return NULL; + assert(type >= 0 || !head); + return head; } static int condition_test_kernel_command_line(Condition *c) { @@ -128,29 +135,77 @@ static int condition_test_kernel_command_line(Condition *c) { return false; } -static int condition_test_kernel_version(Condition *c) { - enum { - /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest - * should be listed first. */ - LOWER_OR_EQUAL, - GREATER_OR_EQUAL, - LOWER, - GREATER, - EQUAL, - _ORDER_MAX, - }; +typedef enum { + /* Listed in order of checking. Note that some comparators are prefixes of others, hence the longest + * should be listed first. */ + ORDER_LOWER_OR_EQUAL, + ORDER_GREATER_OR_EQUAL, + ORDER_LOWER, + ORDER_GREATER, + ORDER_EQUAL, + ORDER_UNEQUAL, + _ORDER_MAX, + _ORDER_INVALID = -1 +} OrderOperator; + +static OrderOperator parse_order(const char **s) { static const char *const prefix[_ORDER_MAX] = { - [LOWER_OR_EQUAL] = "<=", - [GREATER_OR_EQUAL] = ">=", - [LOWER] = "<", - [GREATER] = ">", - [EQUAL] = "=", + [ORDER_LOWER_OR_EQUAL] = "<=", + [ORDER_GREATER_OR_EQUAL] = ">=", + [ORDER_LOWER] = "<", + [ORDER_GREATER] = ">", + [ORDER_EQUAL] = "=", + [ORDER_UNEQUAL] = "!=", }; - const char *p = NULL; + + OrderOperator i; + + for (i = 0; i < _ORDER_MAX; i++) { + const char *e; + + e = startswith(*s, prefix[i]); + if (e) { + *s = e; + return i; + } + } + + return _ORDER_INVALID; +} + +static bool test_order(int k, OrderOperator p) { + + switch (p) { + + case ORDER_LOWER: + return k < 0; + + case ORDER_LOWER_OR_EQUAL: + return k <= 0; + + case ORDER_EQUAL: + return k == 0; + + case ORDER_UNEQUAL: + return k != 0; + + case ORDER_GREATER_OR_EQUAL: + return k >= 0; + + case ORDER_GREATER: + return k > 0; + + default: + assert_not_reached("unknown order"); + + } +} + +static int condition_test_kernel_version(Condition *c) { + OrderOperator order; struct utsname u; - size_t i; - int k; + const char *p; assert(c); assert(c->parameter); @@ -158,38 +213,64 @@ static int condition_test_kernel_version(Condition *c) { assert_se(uname(&u) >= 0); - for (i = 0; i < _ORDER_MAX; i++) { - p = startswith(c->parameter, prefix[i]); - if (p) - break; - } + p = c->parameter; + order = parse_order(&p); /* No prefix? Then treat as glob string */ - if (!p) + if (order < 0) return fnmatch(skip_leading_chars(c->parameter, NULL), u.release, 0) == 0; - k = str_verscmp(u.release, skip_leading_chars(p, NULL)); + return test_order(str_verscmp(u.release, skip_leading_chars(p, NULL)), order); +} - switch (i) { +static int condition_test_memory(Condition *c) { + OrderOperator order; + uint64_t m, k; + const char *p; + int r; - case LOWER: - return k < 0; + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_MEMORY); - case LOWER_OR_EQUAL: - return k <= 0; + m = physical_memory(); - case EQUAL: - return k == 0; + p = c->parameter; + order = parse_order(&p); + if (order < 0) + order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */ - case GREATER_OR_EQUAL: - return k >= 0; + r = safe_atou64(p, &k); + if (r < 0) + return log_debug_errno(r, "Failed to parse size: %m"); - case GREATER: - return k > 0; + return test_order(CMP(m, k), order); +} - default: - assert_not_reached("Can't compare"); - } +static int condition_test_cpus(Condition *c) { + OrderOperator order; + const char *p; + unsigned k; + int r, n; + + assert(c); + assert(c->parameter); + assert(c->type == CONDITION_CPUS); + + n = cpus_in_affinity_mask(); + if (n < 0) + return log_debug_errno(n, "Failed to determine CPUs in affinity mask: %m"); + + p = c->parameter; + order = parse_order(&p); + if (order < 0) + order = ORDER_GREATER_OR_EQUAL; /* default to >= check, if nothing is specified. */ + + r = safe_atou(p, &k); + if (r < 0) + return log_debug_errno(r, "Failed to parse number of CPUs: %m"); + + return test_order(CMP((unsigned) n, k), order); } static int condition_test_user(Condition *c) { @@ -625,6 +706,8 @@ int condition_test(Condition *c) { [CONDITION_GROUP] = condition_test_group, [CONDITION_CONTROL_GROUP_CONTROLLER] = condition_test_control_group_controller, [CONDITION_NULL] = condition_test_null, + [CONDITION_CPUS] = condition_test_cpus, + [CONDITION_MEMORY] = condition_test_memory, }; int r, b; @@ -644,6 +727,52 @@ int condition_test(Condition *c) { return b; } +bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata) { + Condition *c; + int triggered = -1; + + assert(!!logger == !!to_string); + + /* If the condition list is empty, then it is true */ + if (!first) + return true; + + /* Otherwise, if all of the non-trigger conditions apply and + * if any of the trigger conditions apply (unless there are + * none) we return true */ + LIST_FOREACH(conditions, c, first) { + int r; + + r = condition_test(c); + + if (logger) { + if (r < 0) + logger(userdata, LOG_WARNING, r, __FILE__, __LINE__, __func__, + "Couldn't determine result for %s=%s%s%s, assuming failed: %m", + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter); + else + logger(userdata, LOG_DEBUG, 0, __FILE__, __LINE__, __func__, + "%s=%s%s%s %s.", + to_string(c->type), + c->trigger ? "|" : "", + c->negate ? "!" : "", + c->parameter, + condition_result_to_string(c->result)); + } + + if (!c->trigger && r <= 0) + return false; + + if (c->trigger && triggered <= 0) + triggered = r > 0; + } + + return triggered != 0; +} + void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)) { assert(c); assert(f); @@ -690,7 +819,9 @@ static const char* const condition_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_USER] = "ConditionUser", [CONDITION_GROUP] = "ConditionGroup", [CONDITION_CONTROL_GROUP_CONTROLLER] = "ConditionControlGroupController", - [CONDITION_NULL] = "ConditionNull" + [CONDITION_NULL] = "ConditionNull", + [CONDITION_CPUS] = "ConditionCPUs", + [CONDITION_MEMORY] = "ConditionMemory", }; DEFINE_STRING_TABLE_LOOKUP(condition_type, ConditionType); @@ -718,7 +849,9 @@ static const char* const assert_type_table[_CONDITION_TYPE_MAX] = { [CONDITION_USER] = "AssertUser", [CONDITION_GROUP] = "AssertGroup", [CONDITION_CONTROL_GROUP_CONTROLLER] = "AssertControlGroupController", - [CONDITION_NULL] = "AssertNull" + [CONDITION_NULL] = "AssertNull", + [CONDITION_CPUS] = "AssertCPUs", + [CONDITION_MEMORY] = "AssertMemory", }; DEFINE_STRING_TABLE_LOOKUP(assert_type, ConditionType); diff --git a/src/shared/condition.h b/src/shared/condition.h index e69fc366f..84322e742 100644 --- a/src/shared/condition.h +++ b/src/shared/condition.h @@ -16,6 +16,8 @@ typedef enum ConditionType { CONDITION_SECURITY, CONDITION_CAPABILITY, CONDITION_AC_POWER, + CONDITION_MEMORY, + CONDITION_CPUS, CONDITION_NEEDS_UPDATE, CONDITION_FIRST_BOOT, @@ -65,9 +67,14 @@ typedef struct Condition { Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate); void condition_free(Condition *c); -Condition* condition_free_list(Condition *c); +Condition* condition_free_list_type(Condition *first, ConditionType type); +static inline Condition* condition_free_list(Condition *first) { + return condition_free_list_type(first, _CONDITION_TYPE_INVALID); +} int condition_test(Condition *c); +typedef int (*condition_test_logger_t)(void *userdata, int level, int error, const char *file, int line, const char *func, const char *format, ...) _printf_(7, 8); +bool condition_test_list(Condition *first, const char *(*to_string)(ConditionType t), condition_test_logger_t logger, void *userdata); void condition_dump(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); void condition_dump_list(Condition *c, FILE *f, const char *prefix, const char *(*to_string)(ConditionType t)); diff --git a/src/shared/conf-parser.c b/src/shared/conf-parser.c index b80c14780..2df09edaf 100644 --- a/src/shared/conf-parser.c +++ b/src/shared/conf-parser.c @@ -19,6 +19,7 @@ #include "log.h" #include "macro.h" #include "missing.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "process-util.h" @@ -506,6 +507,7 @@ DEFINE_PARSER(unsigned, unsigned, safe_atou); DEFINE_PARSER(double, double, safe_atod); DEFINE_PARSER(nsec, nsec_t, parse_nsec); DEFINE_PARSER(sec, usec_t, parse_sec); +DEFINE_PARSER(sec_def_infinity, usec_t, parse_sec_def_infinity); DEFINE_PARSER(mode, mode_t, parse_mode); int config_parse_iec_size(const char* unit, diff --git a/src/shared/conf-parser.h b/src/shared/conf-parser.h index 865db4278..17b4bdf1a 100644 --- a/src/shared/conf-parser.h +++ b/src/shared/conf-parser.h @@ -127,6 +127,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_string); CONFIG_PARSER_PROTOTYPE(config_parse_path); CONFIG_PARSER_PROTOTYPE(config_parse_strv); CONFIG_PARSER_PROTOTYPE(config_parse_sec); +CONFIG_PARSER_PROTOTYPE(config_parse_sec_def_infinity); CONFIG_PARSER_PROTOTYPE(config_parse_nsec); CONFIG_PARSER_PROTOTYPE(config_parse_mode); CONFIG_PARSER_PROTOTYPE(config_parse_warn_compat); diff --git a/src/shared/crypt-util.h b/src/shared/crypt-util.h index 8c86714ae..bdc2d046e 100644 --- a/src/shared/crypt-util.h +++ b/src/shared/crypt-util.h @@ -11,6 +11,14 @@ #define CRYPT_LUKS NULL #endif +#ifndef CRYPT_ACTIVATE_SAME_CPU_CRYPT +#define CRYPT_ACTIVATE_SAME_CPU_CRYPT (1 << 6) +#endif + +#ifndef CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS +#define CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS (1 << 7) +#endif + DEFINE_TRIVIAL_CLEANUP_FUNC(struct crypt_device *, crypt_free); void cryptsetup_log_glue(int level, const char *msg, void *usrptr); diff --git a/src/shared/dev-setup.c b/src/shared/dev-setup.c index b545c2a1c..12e80e4b9 100644 --- a/src/shared/dev-setup.c +++ b/src/shared/dev-setup.c @@ -8,10 +8,10 @@ #include "dev-setup.h" #include "label.h" #include "log.h" +#include "nulstr-util.h" #include "path-util.h" #include "umask-util.h" #include "user-util.h" -#include "util.h" int dev_setup(const char *prefix, uid_t uid, gid_t gid) { static const char symlinks[] = diff --git a/src/shared/dissect-image.c b/src/shared/dissect-image.c index d34048702..ce210bfd4 100644 --- a/src/shared/dissect-image.c +++ b/src/shared/dissect-image.c @@ -29,6 +29,7 @@ #include "missing.h" #include "mount-util.h" #include "mountpoint-util.h" +#include "nulstr-util.h" #include "os-util.h" #include "path-util.h" #include "process-util.h" @@ -343,10 +344,8 @@ int dissect_image( errno = 0; r = blkid_do_safeprobe(b); - if (IN_SET(r, -2, 1)) { - log_debug("Failed to identify any partition table."); - return -ENOPKG; - } + if (IN_SET(r, -2, 1)) + return log_debug_errno(SYNTHETIC_ERRNO(ENOPKG), "Failed to identify any partition table."); if (r != 0) return -errno ?: -EIO; @@ -497,6 +496,14 @@ int dissect_image( designator = PARTITION_ESP; fstype = "vfat"; + + } else if (sd_id128_equal(type_id, GPT_XBOOTLDR)) { + + if (pflags & GPT_FLAG_NO_AUTO) + continue; + + designator = PARTITION_XBOOTLDR; + rw = !(pflags & GPT_FLAG_READ_ONLY); } #ifdef GPT_ROOT_NATIVE else if (sd_id128_equal(type_id, GPT_ROOT_NATIVE)) { @@ -612,21 +619,53 @@ int dissect_image( } else if (is_mbr) { - if (pflags != 0x80) /* Bootable flag */ - continue; + switch (blkid_partition_get_type(pp)) { - if (blkid_partition_get_type(pp) != 0x83) /* Linux partition */ - continue; + case 0x83: /* Linux partition */ - if (generic_node) - multiple_generic = true; - else { - generic_nr = nr; - generic_rw = true; - generic_node = strdup(node); - if (!generic_node) + if (pflags != 0x80) /* Bootable flag */ + continue; + + if (generic_node) + multiple_generic = true; + else { + generic_nr = nr; + generic_rw = true; + generic_node = strdup(node); + if (!generic_node) + return -ENOMEM; + } + + break; + + case 0xEA: { /* Boot Loader Spec extended $BOOT partition */ + _cleanup_free_ char *n = NULL; + sd_id128_t id = SD_ID128_NULL; + const char *sid; + + /* First one wins */ + if (m->partitions[PARTITION_XBOOTLDR].found) + continue; + + sid = blkid_partition_get_uuid(pp); + if (sid) + (void) sd_id128_from_string(sid, &id); + + n = strdup(node); + if (!n) return -ENOMEM; - } + + m->partitions[PARTITION_XBOOTLDR] = (DissectedPartition) { + .found = true, + .partno = nr, + .rw = true, + .architecture = _ARCHITECTURE_INVALID, + .node = TAKE_PTR(n), + .uuid = id, + }; + + break; + }} } } @@ -819,11 +858,15 @@ static int mount_partition( return -ENOMEM; } - return mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options); + r = mount_verbose(LOG_DEBUG, node, p, fstype, MS_NODEV|(rw ? 0 : MS_RDONLY), options); + if (r < 0) + return r; + + return 1; } int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, DissectImageFlags flags) { - int r; + int r, boot_mounted; assert(m); assert(where); @@ -856,21 +899,26 @@ int dissected_image_mount(DissectedImage *m, const char *where, uid_t uid_shift, if (r < 0) return r; + boot_mounted = mount_partition(m->partitions + PARTITION_XBOOTLDR, where, "/boot", uid_shift, flags); + if (boot_mounted < 0) + return boot_mounted; + if (m->partitions[PARTITION_ESP].found) { - const char *mp; + /* Mount the ESP to /efi if it exists. If it doesn't exist, use /boot instead, but only if it + * exists and is empty, and we didn't already mount the XBOOTLDR partition into it. */ - /* Mount the ESP to /efi if it exists and is empty. If it doesn't exist, use /boot instead. */ + r = chase_symlinks("/efi", where, CHASE_PREFIX_ROOT, NULL); + if (r >= 0) { + r = mount_partition(m->partitions + PARTITION_ESP, where, "/efi", uid_shift, flags); + if (r < 0) + return r; - FOREACH_STRING(mp, "/efi", "/boot") { + } else if (boot_mounted <= 0) { _cleanup_free_ char *p = NULL; - r = chase_symlinks(mp, where, CHASE_PREFIX_ROOT, &p); - if (r < 0) - continue; - - r = dir_is_empty(p); - if (r > 0) { - r = mount_partition(m->partitions + PARTITION_ESP, where, mp, uid_shift, flags); + r = chase_symlinks("/boot", where, CHASE_PREFIX_ROOT, &p); + if (r >= 0 && dir_is_empty(p) > 0) { + r = mount_partition(m->partitions + PARTITION_ESP, where, "/boot", uid_shift, flags); if (r < 0) return r; } @@ -1499,6 +1547,7 @@ static const char *const partition_designator_table[] = { [PARTITION_HOME] = "home", [PARTITION_SRV] = "srv", [PARTITION_ESP] = "esp", + [PARTITION_XBOOTLDR] = "xbootldr", [PARTITION_SWAP] = "swap", [PARTITION_ROOT_VERITY] = "root-verity", [PARTITION_ROOT_SECONDARY_VERITY] = "root-secondary-verity", diff --git a/src/shared/dissect-image.h b/src/shared/dissect-image.h index f50b40ea1..ded43f331 100644 --- a/src/shared/dissect-image.h +++ b/src/shared/dissect-image.h @@ -29,6 +29,7 @@ enum { PARTITION_HOME, PARTITION_SRV, PARTITION_ESP, + PARTITION_XBOOTLDR, PARTITION_SWAP, PARTITION_ROOT_VERITY, /* verity data for the PARTITION_ROOT partition */ PARTITION_ROOT_SECONDARY_VERITY, /* verity data for the PARTITION_ROOT_SECONDARY partition */ diff --git a/src/shared/efivars.c b/src/shared/efivars.c index 26f905bfa..4f32163bb 100644 --- a/src/shared/efivars.c +++ b/src/shared/efivars.c @@ -21,11 +21,11 @@ #include "io-util.h" #include "macro.h" #include "parse-util.h" +#include "sort-util.h" #include "stdio-util.h" #include "strv.h" #include "time-util.h" #include "utf8.h" -#include "util.h" #include "virt.h" #if ENABLE_EFI @@ -223,7 +223,7 @@ int efi_get_variable( if (fstat(fd, &st) < 0) return -errno; if (st.st_size < 4) - return -EIO; + return -ENODATA; if (st.st_size > 4*1024*1024 + 4) return -E2BIG; @@ -789,16 +789,6 @@ 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; @@ -903,6 +893,16 @@ int efi_loader_get_features(uint64_t *ret) { #endif +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 "-_."); +} + char *efi_tilt_backslashes(char *s) { char *p; diff --git a/src/shared/efivars.h b/src/shared/efivars.h index 92670c82c..d8f18aae9 100644 --- a/src/shared/efivars.h +++ b/src/shared/efivars.h @@ -50,8 +50,6 @@ 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 @@ -138,4 +136,6 @@ static inline int efi_loader_get_features(uint64_t *ret) { #endif +bool efi_loader_entry_name_valid(const char *s); + char *efi_tilt_backslashes(char *s); diff --git a/src/shared/exec-util.c b/src/shared/exec-util.c index 17a278a00..2867f08a7 100644 --- a/src/shared/exec-util.c +++ b/src/shared/exec-util.c @@ -78,24 +78,28 @@ static int do_execute( void* const callback_args[_STDOUT_CONSUME_MAX], int output_fd, char *argv[], - char *envp[]) { + char *envp[], + ExecDirFlags flags) { _cleanup_hashmap_free_free_ Hashmap *pids = NULL; _cleanup_strv_free_ char **paths = NULL; char **path, **e; int r; + bool parallel_execution; /* We fork this all off from a child process so that we can somewhat cleanly make * use of SIGALRM to set a time limit. * - * If callbacks is nonnull, execution is serial. Otherwise, we default to parallel. + * We attempt to perform parallel execution if configured by the user, however + * if `callbacks` is nonnull, execution must be serial. */ + parallel_execution = FLAGS_SET(flags, EXEC_DIR_PARALLEL) && !callbacks; 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 log_error_errno(r, "Failed to enumerate executables: %m"); - if (!callbacks) { + if (parallel_execution) { pids = hashmap_new(NULL); if (!pids) return log_oom(); @@ -130,23 +134,28 @@ static int do_execute( if (r <= 0) continue; - if (pids) { + if (parallel_execution) { r = hashmap_put(pids, PID_TO_PTR(pid), t); if (r < 0) return log_oom(); t = NULL; } else { r = wait_for_terminate_and_check(t, pid, WAIT_LOG); - if (r < 0) - continue; + if (FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS)) { + if (r < 0) + continue; + } else if (r > 0) + return r; - if (lseek(fd, 0, SEEK_SET) < 0) - return log_error_errno(errno, "Failed to seek on serialization fd: %m"); + if (callbacks) { + if (lseek(fd, 0, SEEK_SET) < 0) + return log_error_errno(errno, "Failed to seek on serialization fd: %m"); - r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]); - fd = -1; - if (r < 0) - return log_error_errno(r, "Failed to process output from %s: %m", *path); + r = callbacks[STDOUT_GENERATE](fd, callback_args[STDOUT_GENERATE]); + fd = -1; + if (r < 0) + return log_error_errno(r, "Failed to process output from %s: %m", *path); + } } } @@ -166,7 +175,9 @@ static int do_execute( t = hashmap_remove(pids, PID_TO_PTR(pid)); assert(t); - (void) wait_for_terminate_and_check(t, pid, WAIT_LOG); + r = wait_for_terminate_and_check(t, pid, WAIT_LOG); + if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0) + return r; } return 0; @@ -178,12 +189,14 @@ int execute_directories( gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], void* const callback_args[_STDOUT_CONSUME_MAX], char *argv[], - char *envp[]) { + char *envp[], + ExecDirFlags flags) { char **dirs = (char**) directories; _cleanup_close_ int fd = -1; char *name; int r; + pid_t executor_pid; assert(!strv_isempty(dirs)); @@ -205,14 +218,20 @@ int execute_directories( * them to finish. Optionally a timeout is applied. If a file with the same name * exists in more than one directory, the earliest one wins. */ - r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG|FORK_WAIT, NULL); + r = safe_fork("(sd-executor)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &executor_pid); if (r < 0) return r; if (r == 0) { - r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp); - _exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS); + r = do_execute(dirs, timeout, callbacks, callback_args, fd, argv, envp, flags); + _exit(r < 0 ? EXIT_FAILURE : r); } + r = wait_for_terminate_and_check("(sd-executor)", executor_pid, 0); + if (r < 0) + return r; + if (!FLAGS_SET(flags, EXEC_DIR_IGNORE_ERRORS) && r > 0) + return r; + if (!callbacks) return 0; diff --git a/src/shared/exec-util.h b/src/shared/exec-util.h index 6ac3c9000..5b75a4022 100644 --- a/src/shared/exec-util.h +++ b/src/shared/exec-util.h @@ -14,12 +14,19 @@ enum { _STDOUT_CONSUME_MAX, }; +typedef enum { + EXEC_DIR_NONE = 0, /* No execdir flags */ + EXEC_DIR_PARALLEL = 1 << 0, /* Execute scripts in parallel, if possible */ + EXEC_DIR_IGNORE_ERRORS = 1 << 1, /* Ignore non-zero exit status of scripts */ +} ExecDirFlags; + int execute_directories( const char* const* directories, usec_t timeout, gather_stdout_callback_t const callbacks[_STDOUT_CONSUME_MAX], void* const callback_args[_STDOUT_CONSUME_MAX], char *argv[], - char *envp[]); + char *envp[], + ExecDirFlags flags); extern const gather_stdout_callback_t gather_environment[_STDOUT_CONSUME_MAX]; diff --git a/src/shared/fdset.c b/src/shared/fdset.c index 5d277328c..ae65286c7 100644 --- a/src/shared/fdset.c +++ b/src/shared/fdset.c @@ -48,25 +48,25 @@ int fdset_new_array(FDSet **ret, const int *fds, size_t n_fds) { return 0; } -FDSet* fdset_free(FDSet *s) { +void fdset_close(FDSet *s) { void *p; while ((p = set_steal_first(MAKE_SET(s)))) { - /* Valgrind's fd might have ended up in this set here, - * due to fdset_new_fill(). We'll ignore all failures - * here, so that the EBADFD that valgrind will return - * us on close() doesn't influence us */ + /* Valgrind's fd might have ended up in this set here, due to fdset_new_fill(). We'll ignore + * all failures here, so that the EBADFD that valgrind will return us on close() doesn't + * influence us */ - /* When reloading duplicates of the private bus - * connection fds and suchlike are closed here, which - * has no effect at all, since they are only - * duplicates. So don't be surprised about these log - * messages. */ + /* When reloading duplicates of the private bus connection fds and suchlike are closed here, + * which has no effect at all, since they are only duplicates. So don't be surprised about + * these log messages. */ - log_debug("Closing left-over fd %i", PTR_TO_FD(p)); - close_nointr(PTR_TO_FD(p)); + log_debug("Closing set fd %i", PTR_TO_FD(p)); + (void) close_nointr(PTR_TO_FD(p)); } +} +FDSet* fdset_free(FDSet *s) { + fdset_close(s); set_free(MAKE_SET(s)); return NULL; } diff --git a/src/shared/fdset.h b/src/shared/fdset.h index d31062b5c..bdf137703 100644 --- a/src/shared/fdset.h +++ b/src/shared/fdset.h @@ -33,6 +33,8 @@ int fdset_iterate(FDSet *s, Iterator *i); int fdset_steal_first(FDSet *fds); +void fdset_close(FDSet *fds); + #define FDSET_FOREACH(fd, fds, i) \ for ((i) = ITERATOR_FIRST, (fd) = fdset_iterate((fds), &(i)); (fd) >= 0; (fd) = fdset_iterate((fds), &(i))) diff --git a/src/shared/format-table.c b/src/shared/format-table.c index 7d529801a..a5c0a99b0 100644 --- a/src/shared/format-table.c +++ b/src/shared/format-table.c @@ -8,9 +8,11 @@ #include "fileio.h" #include "format-table.h" #include "gunicode.h" +#include "memory-util.h" #include "pager.h" #include "parse-util.h" #include "pretty-print.h" +#include "sort-util.h" #include "string-util.h" #include "terminal-util.h" #include "time-util.h" diff --git a/src/shared/fstab-util.c b/src/shared/fstab-util.c index 6fd9866c0..118d591c0 100644 --- a/src/shared/fstab-util.c +++ b/src/shared/fstab-util.c @@ -11,11 +11,11 @@ #include "fstab-util.h" #include "macro.h" #include "mount-util.h" +#include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" #include "string-util.h" #include "strv.h" -#include "util.h" int fstab_has_fstype(const char *fstype) { _cleanup_endmntent_ FILE *f = NULL; diff --git a/src/shared/fstab-util.h b/src/shared/fstab-util.h index 9820f78ca..086225651 100644 --- a/src/shared/fstab-util.h +++ b/src/shared/fstab-util.h @@ -20,14 +20,12 @@ static inline bool fstab_test_option(const char *opts, const char *names) { int fstab_find_pri(const char *options, int *ret); static inline bool fstab_test_yes_no_option(const char *opts, const char *yes_no) { - int r; const char *opt; /* If first name given is last, return 1. * If second name given is last or neither is found, return 0. */ - r = fstab_filter_options(opts, yes_no, &opt, NULL, NULL); - assert(r >= 0); + assert_se(fstab_filter_options(opts, yes_no, &opt, NULL, NULL) >= 0); return opt == yes_no; } diff --git a/src/shared/generator.c b/src/shared/generator.c index 0adaaf2c5..ed7f037e9 100644 --- a/src/shared/generator.c +++ b/src/shared/generator.c @@ -55,13 +55,14 @@ int generator_open_unit_file( return 0; } -int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src) { - /* Adds a symlink from ..d/ to ../ */ +int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src) { + /* Adds a symlink from ./ to (if src is absolute) + * or ../ (otherwise). */ const char *from, *to; - from = strjoina("../", src); - to = strjoina(root, "/", dst, ".", dep_type, "/", src); + from = path_is_absolute(src) ? src : strjoina("../", src); + to = strjoina(dir, "/", dst, ".", dep_type, "/", basename(src)); mkdir_parents_label(to, 0755); if (symlink(from, to) < 0) @@ -85,7 +86,7 @@ static int write_fsck_sysroot_service(const char *dir, const char *what) { if (!escaped2) return log_oom(); - unit = strjoina(dir, "/systemd-fsck-root.service"); + unit = strjoina(dir, "/"SPECIAL_FSCK_ROOT_SERVICE); log_debug("Creating %s", unit); r = unit_name_from_path(what, ".device", &device); @@ -157,10 +158,10 @@ int generator_write_fsck_deps( if (path_equal(where, "/")) { const char *lnk; - lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/systemd-fsck-root.service"); + lnk = strjoina(dir, "/" SPECIAL_LOCAL_FS_TARGET ".wants/"SPECIAL_FSCK_ROOT_SERVICE); mkdir_parents(lnk, 0755); - if (symlink(SYSTEM_DATA_UNIT_PATH "/systemd-fsck-root.service", lnk) < 0) + if (symlink(SYSTEM_DATA_UNIT_PATH "/"SPECIAL_FSCK_ROOT_SERVICE, lnk) < 0) return log_error_errno(errno, "Failed to create symlink %s: %m", lnk); } else { @@ -172,7 +173,7 @@ int generator_write_fsck_deps( if (r < 0) return r; - fsck = "systemd-fsck-root.service"; + fsck = SPECIAL_FSCK_ROOT_SERVICE; } else { r = unit_name_from_path_instance("systemd-fsck", what, ".service", &_fsck); if (r < 0) @@ -390,7 +391,7 @@ int generator_hook_up_mkfs( "Cannot format partition %s, filesystem type is not specified", node); - r = unit_name_from_path_instance("systemd-mkfs", node, ".service", &unit); + r = unit_name_from_path_instance("systemd-makefs", node, ".service", &unit); if (r < 0) return log_error_errno(r, "Failed to make unit instance name from path \"%s\": %m", node); @@ -416,7 +417,7 @@ int generator_hook_up_mkfs( "# Automatically generated by %s\n\n" "[Unit]\n" "Description=Make File System on %%f\n" - "Documentation=man:systemd-mkfs@.service(8)\n" + "Documentation=man:systemd-makefs@.service(8)\n" "DefaultDependencies=no\n" "BindsTo=%%i.device\n" "Conflicts=shutdown.target\n" @@ -498,6 +499,12 @@ int generator_hook_up_growfs( return generator_add_symlink(dir, where_unit, "wants", unit); } +int generator_enable_remount_fs_service(const char *dir) { + /* Pull in systemd-remount-fs.service */ + return generator_add_symlink(dir, SPECIAL_LOCAL_FS_TARGET, "wants", + SYSTEM_DATA_UNIT_PATH "/" SPECIAL_REMOUNT_FS_SERVICE); +} + 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 5a1c1e32f..fa002a911 100644 --- a/src/shared/generator.h +++ b/src/shared/generator.h @@ -11,7 +11,7 @@ int generator_open_unit_file( const char *name, FILE **file); -int generator_add_symlink(const char *root, const char *dst, const char *dep_type, const char *src); +int generator_add_symlink(const char *dir, const char *dst, const char *dep_type, const char *src); int generator_write_fsck_deps( FILE *f, @@ -50,6 +50,8 @@ int generator_hook_up_growfs( const char *where, const char *target); +int generator_enable_remount_fs_service(const char *dir); + void log_setup_generator(void); /* Similar to DEFINE_MAIN_FUNCTION, but initializes logging and assigns positional arguments. */ diff --git a/src/shared/gpt.h b/src/shared/gpt.h index fd953fabc..31e01bd5a 100644 --- a/src/shared/gpt.h +++ b/src/shared/gpt.h @@ -15,6 +15,7 @@ #define GPT_ROOT_ARM_64 SD_ID128_MAKE(b9,21,b0,45,1d,f0,41,c3,af,44,4c,6f,28,0d,3f,ae) #define GPT_ROOT_IA64 SD_ID128_MAKE(99,3d,8d,3d,f8,0e,42,25,85,5a,9d,af,8e,d7,ea,97) #define GPT_ESP SD_ID128_MAKE(c1,2a,73,28,f8,1f,11,d2,ba,4b,00,a0,c9,3e,c9,3b) +#define GPT_XBOOTLDR SD_ID128_MAKE(bc,13,c2,ff,59,e6,42,62,a3,52,b2,75,fd,6f,71,72) #define GPT_SWAP SD_ID128_MAKE(06,57,fd,6d,a4,ab,43,c4,84,e5,09,33,c8,4b,4f,4f) #define GPT_HOME SD_ID128_MAKE(93,3a,c7,e1,2e,b4,4f,13,b8,44,0e,14,e2,ae,f9,15) #define GPT_SRV SD_ID128_MAKE(3b,8f,84,25,20,e0,4f,3b,90,7f,1a,25,a7,6f,98,e8) diff --git a/src/shared/import-util.c b/src/shared/import-util.c index bcd6c0c5e..dad6c1c2f 100644 --- a/src/shared/import-util.c +++ b/src/shared/import-util.c @@ -8,10 +8,10 @@ #include "import-util.h" #include "log.h" #include "macro.h" +#include "nulstr-util.h" #include "path-util.h" #include "string-table.h" #include "string-util.h" -#include "util.h" int import_url_last_component(const char *url, char **ret) { const char *e, *p; diff --git a/src/shared/install.c b/src/shared/install.c index 8629304ce..ca52d17e4 100644 --- a/src/shared/install.c +++ b/src/shared/install.c @@ -15,6 +15,7 @@ #include "alloc-util.h" #include "conf-files.h" #include "conf-parser.h" +#include "def.h" #include "dirent-util.h" #include "extract-word.h" #include "fd-util.h" @@ -116,7 +117,7 @@ bool unit_type_may_template(UnitType type) { UNIT_PATH); } -static const char *unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { +static const char *const unit_file_type_table[_UNIT_FILE_TYPE_MAX] = { [UNIT_FILE_TYPE_REGULAR] = "regular", [UNIT_FILE_TYPE_SYMLINK] = "symlink", [UNIT_FILE_TYPE_MASKED] = "masked", @@ -2796,7 +2797,7 @@ static int split_pattern_into_name_and_instances(const char *pattern, char **out assert(out_instances); assert(out_unit_name); - r = extract_first_word(&pattern, &unit_name, NULL, 0); + r = extract_first_word(&pattern, &unit_name, NULL, EXTRACT_RETAIN_ESCAPE); if (r < 0) return r; @@ -2820,6 +2821,24 @@ static int split_pattern_into_name_and_instances(const char *pattern, char **out return 0; } +static int presets_find_config(UnitFileScope scope, const char *root_dir, char ***files) { + static const char* const system_dirs[] = {CONF_PATHS("systemd/system-preset"), NULL}; + static const char* const user_dirs[] = {CONF_PATHS_USR("systemd/user-preset"), NULL}; + const char* const* dirs; + + assert(scope >= 0); + assert(scope < _UNIT_FILE_SCOPE_MAX); + + if (scope == UNIT_FILE_SYSTEM) + dirs = system_dirs; + else if (IN_SET(scope, UNIT_FILE_GLOBAL, UNIT_FILE_USER)) + dirs = user_dirs; + else + assert_not_reached("Invalid unit file scope"); + + return conf_files_list_strv(files, ".preset", root_dir, 0, dirs); +} + static int read_presets(UnitFileScope scope, const char *root_dir, Presets *presets) { _cleanup_(presets_freep) Presets ps = {}; size_t n_allocated = 0; @@ -2831,33 +2850,7 @@ static int read_presets(UnitFileScope scope, const char *root_dir, Presets *pres assert(scope < _UNIT_FILE_SCOPE_MAX); assert(presets); - switch (scope) { - case UNIT_FILE_SYSTEM: - r = conf_files_list(&files, ".preset", root_dir, 0, - "/etc/systemd/system-preset", - "/run/systemd/system-preset", - "/usr/local/lib/systemd/system-preset", - "/usr/lib/systemd/system-preset", -#if HAVE_SPLIT_USR - "/lib/systemd/system-preset", -#endif - NULL); - break; - - case UNIT_FILE_GLOBAL: - case UNIT_FILE_USER: - r = conf_files_list(&files, ".preset", root_dir, 0, - "/etc/systemd/user-preset", - "/run/systemd/user-preset", - "/usr/local/lib/systemd/user-preset", - "/usr/lib/systemd/user-preset", - NULL); - break; - - default: - assert_not_reached("Invalid unit file scope"); - } - + r = presets_find_config(scope, root_dir, &files); if (r < 0) return r; diff --git a/src/shared/json.c b/src/shared/json.c index 3786ff12b..db003a41a 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include #include @@ -11,6 +12,7 @@ #include "sd-messages.h" #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "float.h" @@ -18,6 +20,7 @@ #include "json-internal.h" #include "json.h" #include "macro.h" +#include "memory-util.h" #include "string-table.h" #include "string-util.h" #include "strv.h" @@ -1756,7 +1759,6 @@ static void inc_lines_columns(unsigned *line, unsigned *column, const char *s, s assert(s || n == 0); while (n > 0) { - if (*s == '\n') { (*line)++; *column = 1; @@ -1765,7 +1767,7 @@ static void inc_lines_columns(unsigned *line, unsigned *column, const char *s, s else { int w; - w = utf8_encoded_valid_unichar(s); + w = utf8_encoded_valid_unichar(s, n); 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 */ @@ -1930,7 +1932,7 @@ static int json_parse_string(const char **p, char **ret) { continue; } - len = utf8_encoded_valid_unichar(c); + len = utf8_encoded_valid_unichar(c, (size_t) -1); if (len < 0) return len; @@ -3109,8 +3111,6 @@ finish: free(stack); - va_end(ap); - return r; } @@ -3167,7 +3167,7 @@ int json_log_internal( "CONFIG_FILE=%s", source, "CONFIG_LINE=%u", source_line, "CONFIG_COLUMN=%u", source_column, - LOG_MESSAGE("%s:%u: %s", source, line, buffer), + LOG_MESSAGE("%s:%u:%u: %s", source, source_line, source_column, buffer), NULL); else return log_struct_internal( @@ -3297,10 +3297,8 @@ int json_dispatch_boolean(const char *name, JsonVariant *variant, JsonDispatchFl 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; - } + if (!json_variant_is_boolean(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name)); *b = json_variant_boolean(variant); return 0; @@ -3312,10 +3310,8 @@ int json_dispatch_tristate(const char *name, JsonVariant *variant, JsonDispatchF 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; - } + if (!json_variant_is_boolean(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a boolean.", strna(name)); *b = json_variant_boolean(variant); return 0; @@ -3327,10 +3323,8 @@ int json_dispatch_integer(const char *name, JsonVariant *variant, JsonDispatchFl 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_is_integer(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); *i = json_variant_integer(variant); return 0; @@ -3342,10 +3336,8 @@ int json_dispatch_unsigned(const char *name, JsonVariant *variant, JsonDispatchF 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_is_unsigned(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name)); *u = json_variant_unsigned(variant); return 0; @@ -3357,15 +3349,11 @@ int json_dispatch_uint32(const char *name, JsonVariant *variant, JsonDispatchFla 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_is_unsigned(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an unsigned integer.", strna(name)); - if (json_variant_unsigned(variant) > UINT32_MAX) { - json_log(variant, flags, 0, "JSON field '%s' out of bounds.", strna(name)); - return -ERANGE; - } + if (json_variant_unsigned(variant) > UINT32_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); *u = (uint32_t) json_variant_unsigned(variant); return 0; @@ -3377,15 +3365,11 @@ int json_dispatch_int32(const char *name, JsonVariant *variant, JsonDispatchFlag 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_is_integer(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an integer.", strna(name)); - 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; - } + if (json_variant_integer(variant) < INT32_MIN || json_variant_integer(variant) > INT32_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); *i = (int32_t) json_variant_integer(variant); return 0; @@ -3403,10 +3387,8 @@ int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFla return 0; } - if (!json_variant_is_string(variant)) { - json_log(variant, flags, 0, "JSON field '%s' is not a string.", strna(name)); - return -EINVAL; - } + if (!json_variant_is_string(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); r = free_and_strdup(s, json_variant_string(variant)); if (r < 0) @@ -3429,20 +3411,16 @@ int json_dispatch_strv(const char *name, JsonVariant *variant, JsonDispatchFlags return 0; } - if (!json_variant_is_array(variant)) { - json_log(variant, 0, flags, "JSON field '%s' is not an array.", strna(name)); - return -EINVAL; - } + if (!json_variant_is_array(variant)) + return json_log(variant, SYNTHETIC_ERRNO(EINVAL), flags, "JSON field '%s' is not an array.", strna(name)); 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; - } + if (!json_variant_is_string(e)) + return json_log(e, flags, SYNTHETIC_ERRNO(EINVAL), "JSON array element is not a string."); r = strv_extend(&l, json_variant_string(e)); if (r < 0) diff --git a/src/shared/json.h b/src/shared/json.h index f8e035cda..e5532c506 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -4,10 +4,11 @@ #include #include #include +#include #include "macro.h" #include "string-util.h" -#include "util.h" +#include "log.h" /* In case you wonder why we have our own JSON implementation, here are a couple of reasons why this implementation has @@ -264,12 +265,12 @@ static inline int json_dispatch_level(JsonDispatchFlags flags) { 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, ...) \ +#define json_log(variant, flags, error, ...) \ ({ \ - int _level = json_dispatch_level(flags), _e = (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); \ + : -ERRNO_VALUE(_e); \ }) #define JSON_VARIANT_STRING_CONST(x) _JSON_VARIANT_STRING_CONST(UNIQ, (x)) diff --git a/src/shared/libmount-util.h b/src/shared/libmount-util.h new file mode 100644 index 000000000..7d94468e5 --- /dev/null +++ b/src/shared/libmount-util.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +/* This needs to be after sys/mount.h */ +#include + +#include "macro.h" + +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); +DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); diff --git a/src/shared/log-link.h b/src/shared/log-link.h new file mode 100644 index 000000000..99efc0cba --- /dev/null +++ b/src/shared/log-link.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "log.h" + +/* + * The following macros append INTERFACE= to the message. + * The macros require a struct named 'Link' which contains 'char *ifname': + * + * typedef struct Link { + * char *ifname; + * } Link; + * + * See, network/networkd-link.h for example. + */ + +#define log_link_full(link, level, error, ...) \ + ({ \ + const Link *_l = (link); \ + (_l && _l->ifname) ? log_object_internal(level, error, __FILE__, __LINE__, __func__, "INTERFACE=", _l->ifname, NULL, NULL, ##__VA_ARGS__) : \ + log_internal(level, error, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ + }) \ + +#define log_link_debug(link, ...) log_link_full(link, LOG_DEBUG, 0, ##__VA_ARGS__) +#define log_link_info(link, ...) log_link_full(link, LOG_INFO, 0, ##__VA_ARGS__) +#define log_link_notice(link, ...) log_link_full(link, LOG_NOTICE, 0, ##__VA_ARGS__) +#define log_link_warning(link, ...) log_link_full(link, LOG_WARNING, 0, ##__VA_ARGS__) +#define log_link_error(link, ...) log_link_full(link, LOG_ERR, 0, ##__VA_ARGS__) + +#define log_link_debug_errno(link, error, ...) log_link_full(link, LOG_DEBUG, error, ##__VA_ARGS__) +#define log_link_info_errno(link, error, ...) log_link_full(link, LOG_INFO, error, ##__VA_ARGS__) +#define log_link_notice_errno(link, error, ...) log_link_full(link, LOG_NOTICE, error, ##__VA_ARGS__) +#define log_link_warning_errno(link, error, ...) log_link_full(link, LOG_WARNING, error, ##__VA_ARGS__) +#define log_link_error_errno(link, error, ...) log_link_full(link, LOG_ERR, error, ##__VA_ARGS__) + +#define LOG_LINK_MESSAGE(link, fmt, ...) "MESSAGE=%s: " fmt, (link)->ifname, ##__VA_ARGS__ +#define LOG_LINK_INTERFACE(link) "INTERFACE=%s", (link)->ifname diff --git a/src/shared/logs-show.c b/src/shared/logs-show.c index 15ef0f19f..5fb736f63 100644 --- a/src/shared/logs-show.c +++ b/src/shared/logs-show.c @@ -25,6 +25,7 @@ #include "log.h" #include "logs-show.h" #include "macro.h" +#include "namespace-util.h" #include "output-mode.h" #include "parse-util.h" #include "process-util.h" diff --git a/src/shared/machine-image.c b/src/shared/machine-image.c index af06ab22e..4ad112740 100644 --- a/src/shared/machine-image.c +++ b/src/shared/machine-image.c @@ -31,6 +31,7 @@ #include "machine-image.h" #include "macro.h" #include "mkdir.h" +#include "nulstr-util.h" #include "os-util.h" #include "path-util.h" #include "rm-rf.h" @@ -39,7 +40,6 @@ #include "strv.h" #include "time-util.h" #include "utf8.h" -#include "util.h" #include "xattr-util.h" static const char* const image_search_path[_IMAGE_CLASS_MAX] = { @@ -808,7 +808,7 @@ static int clone_auxiliary_file(const char *path, const char *new_name, const ch if (!rs) return -ENOMEM; - return copy_file_atomic(path, rs, 0664, 0, COPY_REFLINK); + return copy_file_atomic(path, rs, 0664, 0, 0, COPY_REFLINK); } int image_clone(Image *i, const char *new_name, bool read_only) { @@ -870,7 +870,7 @@ int image_clone(Image *i, const char *new_name, bool read_only) { case IMAGE_RAW: new_path = strjoina("/var/lib/machines/", new_name, ".raw"); - r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, COPY_REFLINK); + r = copy_file_atomic(i->path, new_path, read_only ? 0444 : 0644, FS_NOCOW_FL, FS_NOCOW_FL, COPY_REFLINK|COPY_CRTIME); break; case IMAGE_BLOCK: @@ -1032,7 +1032,7 @@ int image_path_lock(const char *path, int operation, LockFile *global, LockFile } if (p) { - mkdir_p("/run/systemd/nspawn/locks", 0700); + (void) mkdir_p("/run/systemd/nspawn/locks", 0700); r = make_lock_file(p, operation, global); if (r < 0) { @@ -1169,8 +1169,6 @@ int image_read_metadata(Image *i) { } int image_name_lock(const char *name, int operation, LockFile *ret) { - const char *p; - assert(name); assert(ret); @@ -1187,9 +1185,8 @@ int image_name_lock(const char *name, int operation, LockFile *ret) { if (streq(name, ".host")) return -EBUSY; - mkdir_p("/run/systemd/nspawn/locks", 0700); - p = strjoina("/run/systemd/nspawn/locks/name-", name); - + const char *p = strjoina("/run/systemd/nspawn/locks/name-", name); + (void) mkdir_p("/run/systemd/nspawn/locks", 0700); return make_lock_file(p, operation, ret); } diff --git a/src/shared/main-func.h b/src/shared/main-func.h index 3c182e802..1b77316d8 100644 --- a/src/shared/main-func.h +++ b/src/shared/main-func.h @@ -8,17 +8,19 @@ #include "spawn-ask-password-agent.h" #include "spawn-polkit-agent.h" #include "static-destruct.h" +#include "util.h" #define _DEFINE_MAIN_FUNCTION(intro, impl, ret) \ int main(int argc, char *argv[]) { \ int r; \ + save_argc_argv(argc, argv); \ intro; \ r = impl; \ - static_destruct(); \ ask_password_agent_close(); \ polkit_agent_close(); \ - mac_selinux_finish(); \ pager_close(); \ + mac_selinux_finish(); \ + static_destruct(); \ return ret; \ } diff --git a/src/shared/meson.build b/src/shared/meson.build index 99d6ba14f..e05fbce30 100644 --- a/src/shared/meson.build +++ b/src/shared/meson.build @@ -21,10 +21,14 @@ shared_sources = files(''' bootspec.h bpf-program.c bpf-program.h + bus-unit-procs.c + bus-unit-procs.h bus-unit-util.c bus-unit-util.h bus-util.c bus-util.h + bus-wait-for-jobs.c + bus-wait-for-jobs.h calendarspec.c calendarspec.h cgroup-show.c @@ -91,8 +95,10 @@ shared_sources = files(''' json-internal.h json.c json.h + libmount-util.h lockfile-util.c lockfile-util.h + log-link.h logs-show.c logs-show.h loop-util.c @@ -117,6 +123,7 @@ shared_sources = files(''' pager.h path-lookup.c path-lookup.h + pe-header.h pretty-print.c pretty-print.h ptyfwd.c diff --git a/src/shared/mount-util.c b/src/shared/mount-util.c index 9fa995f69..9987b6f80 100644 --- a/src/shared/mount-util.c +++ b/src/shared/mount-util.c @@ -29,8 +29,8 @@ #include "strv.h" int umount_recursive(const char *prefix, int flags) { - bool again; int n = 0, r; + bool again; /* Try to umount everything recursively below a * directory. Also, take care of stacked mounts, and keep @@ -73,9 +73,9 @@ int umount_recursive(const char *prefix, int flags) { continue; } - r = cunescape(path, UNESCAPE_RELAX, &p); - if (r < 0) - return r; + k = cunescape(path, UNESCAPE_RELAX, &p); + if (k < 0) + return k; if (!path_startswith(p, prefix)) continue; @@ -95,7 +95,7 @@ int umount_recursive(const char *prefix, int flags) { } while (again); - return r ? r : n; + return r < 0 ? r : n; } static int get_mount_flags(const char *path, unsigned long *flags) { @@ -107,10 +107,15 @@ static int get_mount_flags(const char *path, unsigned long *flags) { return 0; } -/* Use this function only if do you have direct access to /proc/self/mountinfo - * and need the caller to open it for you. This is the case when /proc is - * masked or not mounted. Otherwise, use bind_remount_recursive. */ -int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **blacklist, FILE *proc_self_mountinfo) { +/* Use this function only if you do not have direct access to /proc/self/mountinfo but the caller can open it + * for you. This is the case when /proc is masked or not mounted. Otherwise, use bind_remount_recursive. */ +int bind_remount_recursive_with_mountinfo( + const char *prefix, + unsigned long new_flags, + unsigned long flags_mask, + char **blacklist, + FILE *proc_self_mountinfo) { + _cleanup_set_free_free_ Set *done = NULL; _cleanup_free_ char *cleaned = NULL; int r; @@ -245,16 +250,12 @@ 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, cleaned, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) + if (mount(NULL, cleaned, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) return -errno; log_debug("Made top-level directory %s a mount point.", prefix); - x = strdup(cleaned); - if (!x) - return -ENOMEM; - - r = set_consume(done, x); + r = set_put_strdup(done, cleaned); if (r < 0) return r; } @@ -291,7 +292,7 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl (void) get_mount_flags(x, &orig_flags); orig_flags &= ~MS_RDONLY; - if (mount(NULL, x, NULL, orig_flags|MS_BIND|MS_REMOUNT|(ro ? MS_RDONLY : 0), NULL) < 0) + if (mount(NULL, x, NULL, (orig_flags & ~flags_mask)|MS_BIND|MS_REMOUNT|new_flags, NULL) < 0) return -errno; log_debug("Remounted %s read-only.", x); @@ -299,7 +300,7 @@ int bind_remount_recursive_with_mountinfo(const char *prefix, bool ro, char **bl } } -int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { +int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist) { _cleanup_fclose_ FILE *proc_self_mountinfo = NULL; proc_self_mountinfo = fopen("/proc/self/mountinfo", "re"); @@ -308,7 +309,7 @@ int bind_remount_recursive(const char *prefix, bool ro, char **blacklist) { (void) __fsetlocking(proc_self_mountinfo, FSETLOCKING_BYCALLER); - return bind_remount_recursive_with_mountinfo(prefix, ro, blacklist, proc_self_mountinfo); + return bind_remount_recursive_with_mountinfo(prefix, new_flags, flags_mask, blacklist, proc_self_mountinfo); } int mount_move_root(const char *path) { diff --git a/src/shared/mount-util.h b/src/shared/mount-util.h index 00df1b0e5..8649fca39 100644 --- a/src/shared/mount-util.h +++ b/src/shared/mount-util.h @@ -8,8 +8,8 @@ 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); +int bind_remount_recursive(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist); +int bind_remount_recursive_with_mountinfo(const char *prefix, unsigned long new_flags, unsigned long flags_mask, char **blacklist, FILE *proc_self_mountinfo); int mount_move_root(const char *path); diff --git a/src/shared/pager.c b/src/shared/pager.c index bf2597e65..2abb0f62d 100644 --- a/src/shared/pager.c +++ b/src/shared/pager.c @@ -24,6 +24,7 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" +#include "util.h" static pid_t pager_pid = 0; diff --git a/src/shared/pe-header.h b/src/shared/pe-header.h new file mode 100644 index 000000000..a36291752 --- /dev/null +++ b/src/shared/pe-header.h @@ -0,0 +1,59 @@ +#pragma once + +#include + +#include "macro.h" +#include "sparse-endian.h" + +struct DosFileHeader { + uint8_t Magic[2]; + le16_t LastSize; + le16_t nBlocks; + le16_t nReloc; + le16_t HdrSize; + le16_t MinAlloc; + le16_t MaxAlloc; + le16_t ss; + le16_t sp; + le16_t Checksum; + le16_t ip; + le16_t cs; + le16_t RelocPos; + le16_t nOverlay; + le16_t reserved[4]; + le16_t OEMId; + le16_t OEMInfo; + le16_t reserved2[10]; + le32_t ExeHeader; +} _packed_; + +#define PE_HEADER_MACHINE_I386 0x014cU +#define PE_HEADER_MACHINE_X64 0x8664U + +struct PeFileHeader { + le16_t Machine; + le16_t NumberOfSections; + le32_t TimeDateStamp; + le32_t PointerToSymbolTable; + le32_t NumberOfSymbols; + le16_t SizeOfOptionalHeader; + le16_t Characteristics; +} _packed_; + +struct PeHeader { + uint8_t Magic[4]; + struct PeFileHeader FileHeader; +} _packed_; + +struct PeSectionHeader { + uint8_t Name[8]; + le32_t VirtualSize; + le32_t VirtualAddress; + le32_t SizeOfRawData; + le32_t PointerToRawData; + le32_t PointerToRelocations; + le32_t PointerToLinenumbers; + le16_t NumberOfRelocations; + le16_t NumberOfLinenumbers; + le32_t Characteristics; + } _packed_; diff --git a/src/shared/pretty-print.c b/src/shared/pretty-print.c index de6274a3d..c602e036e 100644 --- a/src/shared/pretty-print.c +++ b/src/shared/pretty-print.c @@ -213,33 +213,91 @@ void print_separator(void) { fputs("\n\n", stdout); } +static int guess_type(const char **name, bool *is_usr, bool *is_collection, const char **extension) { + /* Try to figure out if name is like tmpfiles.d/ or systemd/system-presets/, + * i.e. a collection of directories without a main config file. */ + + _cleanup_free_ char *n = NULL; + bool usr = false, coll = false; + const char *ext = ".conf"; + + if (path_equal(*name, "environment.d")) + /* Special case: we need to include /etc/environment in the search path, even + * though the whole concept is called environment.d. */ + *name = "environment"; + + n = strdup(*name); + if (!n) + return log_oom(); + + delete_trailing_chars(n, "/"); + + if (endswith(n, ".d")) + coll = true; + + if (path_equal(n, "environment")) + usr = true; + + if (path_equal(n, "udev/hwdb.d")) + ext = ".hwdb"; + + if (path_equal(n, "udev/rules.d")) + ext = ".rules"; + + if (path_equal(n, "kernel/install.d")) + ext = ".install"; + + if (PATH_IN_SET(n, "systemd/system-preset", "systemd/user-preset")) { + coll = true; + ext = ".preset"; + } + + if (path_equal(n, "systemd/user-preset")) + usr = true; + + *is_usr = usr; + *is_collection = coll; + *extension = ext; + 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 **dir; + bool is_usr, is_collection; + const char *extension; char **t; int r; - NULSTR_FOREACH(dir, CONF_PATHS_NULSTR("")) { - assert(endswith(dir, "/")); - r = strv_extendf(&dirs, "%s%s.d", dir, name); + r = guess_type(&name, &is_usr, &is_collection, &extension); + if (r < 0) + return r; + + STRV_FOREACH(dir, is_usr ? CONF_PATHS_USR_STRV("") : CONF_PATHS_STRV("")) { + assert(endswith(*dir, "/")); + r = strv_extendf(&dirs, "%s%s%s", *dir, name, + is_collection ? "" : ".d"); 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); + r = conf_files_list_strv(&files, extension, 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 (!is_collection) { + path = path_join(root, "/etc", name); + if (!path) + return log_oom(); + } if (DEBUG_LOGGING) { log_debug("Looking for configuration in:"); - log_debug(" %s", path); + if (path) + log_debug(" %s", path); STRV_FOREACH(t, dirs) - log_debug(" %s/*.conf", *t); + log_debug(" %s/*%s", *t, extension); } /* show */ diff --git a/src/shared/reboot-util.c b/src/shared/reboot-util.c index ca40159b9..08569e8bf 100644 --- a/src/shared/reboot-util.c +++ b/src/shared/reboot-util.c @@ -12,10 +12,13 @@ #include "umask-util.h" #include "virt.h" -int update_reboot_parameter_and_warn(const char *parameter) { +int update_reboot_parameter_and_warn(const char *parameter, bool keep) { int r; if (isempty(parameter)) { + if (keep) + return 0; + if (unlink("/run/systemd/reboot-param") < 0) { if (errno == ENOENT) return 0; @@ -36,6 +39,18 @@ int update_reboot_parameter_and_warn(const char *parameter) { return 0; } +int read_reboot_parameter(char **parameter) { + int r; + + assert(parameter); + + r = read_one_line_file("/run/systemd/reboot-param", parameter); + if (r < 0 && r != -ENOENT) + return log_debug_errno(r, "Failed to read /run/systemd/reboot-param: %m"); + + return 0; +} + int reboot_with_parameter(RebootFlags flags) { int r; diff --git a/src/shared/reboot-util.h b/src/shared/reboot-util.h index d459333ef..7bddc91ea 100644 --- a/src/shared/reboot-util.h +++ b/src/shared/reboot-util.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once -int update_reboot_parameter_and_warn(const char *parameter); +int update_reboot_parameter_and_warn(const char *parameter, bool keep); typedef enum RebootFlags { REBOOT_LOG = 1 << 0, /* log about what we are going to do and all errors */ @@ -9,4 +9,5 @@ typedef enum RebootFlags { REBOOT_FALLBACK = 1 << 2, /* fallback to plain reboot() if argument-based reboot doesn't work, isn't configured or doesn't apply otherwise */ } RebootFlags; +int read_reboot_parameter(char **parameter); int reboot_with_parameter(RebootFlags flags); diff --git a/src/shared/seccomp-util.c b/src/shared/seccomp-util.c index cc58b3c07..8ed09fd49 100644 --- a/src/shared/seccomp-util.c +++ b/src/shared/seccomp-util.c @@ -1,24 +1,26 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include #include #include #include #include #include +#include #include "af-list.h" #include "alloc-util.h" +#include "errno-list.h" #include "macro.h" #include "nsflags.h" +#include "nulstr-util.h" #include "process-util.h" #include "seccomp-util.h" #include "set.h" #include "string-util.h" #include "strv.h" -#include "util.h" -#include "errno-list.h" const uint32_t seccomp_local_archs[] = { @@ -291,6 +293,7 @@ const SyscallFilterSet syscall_filter_sets[_SYSCALL_FILTER_SET_MAX] = { "pause\0" "prlimit64\0" "restart_syscall\0" + "rseq\0" "rt_sigreturn\0" "sched_yield\0" "set_robust_list\0" @@ -1013,7 +1016,7 @@ int seccomp_load_syscall_filter_set_raw(uint32_t default_action, Hashmap* set, u return 0; } -int seccomp_parse_syscall_filter_full( +int seccomp_parse_syscall_filter( const char *name, int errno_num, Hashmap *filter, @@ -1046,7 +1049,7 @@ int seccomp_parse_syscall_filter_full( * away the SECCOMP_PARSE_LOG flag) since any issues in the group table are our own problem, * not a problem in user configuration data and we shouldn't pretend otherwise by complaining * about them. */ - r = seccomp_parse_syscall_filter_full(i, errno_num, filter, flags &~ SECCOMP_PARSE_LOG, unit, filename, line); + r = seccomp_parse_syscall_filter(i, errno_num, filter, flags &~ SECCOMP_PARSE_LOG, unit, filename, line); if (r < 0) return r; } @@ -1502,14 +1505,9 @@ static int add_seccomp_syscall_filter(scmp_filter_ctx seccomp, assert_cc(SCMP_SYS(shmget) > 0); assert_cc(SCMP_SYS(shmat) > 0); assert_cc(SCMP_SYS(shmdt) > 0); -#elif defined(__i386__) || defined(__powerpc64__) -assert_cc(SCMP_SYS(shmget) < 0); -assert_cc(SCMP_SYS(shmat) < 0); -assert_cc(SCMP_SYS(shmdt) < 0); #endif int seccomp_memory_deny_write_execute(void) { - uint32_t arch; int r; @@ -1524,6 +1522,7 @@ int seccomp_memory_deny_write_execute(void) { case SCMP_ARCH_X86: filter_syscall = SCMP_SYS(mmap2); block_syscall = SCMP_SYS(mmap); + shmat_syscall = SCMP_SYS(shmat); break; case SCMP_ARCH_PPC: @@ -1590,7 +1589,7 @@ int seccomp_memory_deny_write_execute(void) { continue; #endif - if (shmat_syscall != 0) { + if (shmat_syscall > 0) { r = add_seccomp_syscall_filter(seccomp, arch, SCMP_SYS(shmat), 1, SCMP_A2(SCMP_CMP_MASKED_EQ, SHM_EXEC, SHM_EXEC)); @@ -1762,3 +1761,206 @@ int seccomp_lock_personality(unsigned long personality) { return 0; } + +int seccomp_protect_hostname(void) { + uint32_t arch; + int r; + + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); + if (r < 0) + return r; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(sethostname), + 0); + if (r < 0) { + log_debug_errno(r, "Failed to add sethostname() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + continue; + } + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(setdomainname), + 0); + if (r < 0) { + log_debug_errno(r, "Failed to add setdomainname() rule for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + continue; + } + + r = seccomp_load(seccomp); + if (IN_SET(r, -EPERM, -EACCES)) + return r; + if (r < 0) + log_debug_errno(r, "Failed to apply hostname restrictions for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + } + + return 0; +} + +static int seccomp_restrict_sxid(scmp_filter_ctx seccomp, mode_t m) { + /* Checks the mode_t parameter of the following system calls: + * + * → chmod() + fchmod() + fchmodat() + * → open() + creat() + openat() + * → mkdir() + mkdirat() + * → mknod() + mknodat() + * + * Returns error if *everything* failed, and 0 otherwise. + */ + int r = 0; + bool any = false; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(chmod), + 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for chmod: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(fchmod), + 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for fchmod: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(fchmodat), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for fchmodat: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(mkdir), + 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for mkdir: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(mkdirat), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for mkdirat: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(mknod), + 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for mknod: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(mknodat), + 1, + SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for mknodat: %m"); + else + any = true; + +#if SCMP_SYS(open) > 0 + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(open), + 2, + SCMP_A1(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT), + SCMP_A2(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for open: %m"); + else + any = true; +#endif + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(openat), + 2, + SCMP_A2(SCMP_CMP_MASKED_EQ, O_CREAT, O_CREAT), + SCMP_A3(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for openat: %m"); + else + any = true; + + r = seccomp_rule_add_exact( + seccomp, + SCMP_ACT_ERRNO(EPERM), + SCMP_SYS(creat), + 1, + SCMP_A1(SCMP_CMP_MASKED_EQ, m, m)); + if (r < 0) + log_debug_errno(r, "Failed to add filter for creat: %m"); + else + any = true; + + return any ? 0 : r; +} + +int seccomp_restrict_suid_sgid(void) { + uint32_t arch; + int r, k; + + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + _cleanup_(seccomp_releasep) scmp_filter_ctx seccomp = NULL; + + r = seccomp_init_for_arch(&seccomp, arch, SCMP_ACT_ALLOW); + if (r < 0) + return r; + + r = seccomp_restrict_sxid(seccomp, S_ISUID); + if (r < 0) + log_debug_errno(r, "Failed to add suid rule for architecture %s, ignoring: %m", seccomp_arch_to_string(arch)); + + k = seccomp_restrict_sxid(seccomp, S_ISGID); + if (k < 0) + log_debug_errno(r, "Failed to add sgid rule for architecture %s, ignoring: %m", seccomp_arch_to_string(arch)); + + if (r < 0 && k < 0) + continue; + + r = seccomp_load(seccomp); + if (IN_SET(r, -EPERM, -EACCES)) + return r; + if (r < 0) + log_debug_errno(r, "Failed to apply suid/sgid restrictions for architecture %s, skipping: %m", seccomp_arch_to_string(arch)); + } + + return 0; +} diff --git a/src/shared/seccomp-util.h b/src/shared/seccomp-util.h index d8a36c4e2..14dbc4269 100644 --- a/src/shared/seccomp-util.h +++ b/src/shared/seccomp-util.h @@ -70,13 +70,13 @@ typedef enum SeccompParseFlags { SECCOMP_PARSE_PERMISSIVE = 1 << 3, } SeccompParseFlags; -int seccomp_parse_syscall_filter_full( - const char *name, int errno_num, Hashmap *filter, SeccompParseFlags flags, - const char *unit, const char *filename, unsigned line); - -static inline int seccomp_parse_syscall_filter(const char *name, int errno_num, Hashmap *filter, SeccompParseFlags flags) { - return seccomp_parse_syscall_filter_full(name, errno_num, filter, flags, NULL, NULL, 0); -} +int seccomp_parse_syscall_filter( + const char *name, + int errno_num, + Hashmap *filter, + SeccompParseFlags flags, + const char *unit, + const char *filename, unsigned line); int seccomp_restrict_archs(Set *archs); int seccomp_restrict_namespaces(unsigned long retain); @@ -85,6 +85,8 @@ int seccomp_restrict_address_families(Set *address_families, bool whitelist); int seccomp_restrict_realtime(void); int seccomp_memory_deny_write_execute(void); int seccomp_lock_personality(unsigned long personality); +int seccomp_protect_hostname(void); +int seccomp_restrict_suid_sgid(void); extern const uint32_t seccomp_local_archs[]; diff --git a/src/shared/serialize.h b/src/shared/serialize.h index 4cbd98bb3..8f4efc799 100644 --- a/src/shared/serialize.h +++ b/src/shared/serialize.h @@ -5,6 +5,7 @@ #include "fdset.h" #include "macro.h" +#include "time-util.h" int serialize_item(FILE *f, const char *key, const char *value); int serialize_item_escaped(FILE *f, const char *key, const char *value); diff --git a/src/shared/sleep-config.c b/src/shared/sleep-config.c index 2e22bd0bb..7f23e5410 100644 --- a/src/shared/sleep-config.c +++ b/src/shared/sleep-config.c @@ -4,12 +4,15 @@ ***/ #include +#include #include #include #include #include #include #include +#include +#include #include #include @@ -17,6 +20,7 @@ #include "conf-parser.h" #include "def.h" #include "env-util.h" +#include "errno-util.h" #include "fd-util.h" #include "fileio.h" #include "log.h" @@ -201,8 +205,7 @@ int find_hibernate_location(char **device, char **type, size_t *size, size_t *us if (!f) { log_full(errno == ENOENT ? LOG_DEBUG : LOG_WARNING, "Failed to retrieve open /proc/swaps: %m"); - assert(errno > 0); - return -errno; + return negative_errno(); } (void) fscanf(f, "%*s %*s %*s %*s %*s\n"); diff --git a/src/shared/specifier.c b/src/shared/specifier.c index b8f7537f7..c8e789680 100644 --- a/src/shared/specifier.c +++ b/src/shared/specifier.c @@ -10,6 +10,7 @@ #include "sd-id128.h" #include "alloc-util.h" +#include "format-util.h" #include "fs-util.h" #include "hostname-util.h" #include "macro.h" diff --git a/src/shared/sysctl-util.c b/src/shared/sysctl-util.c index 480e6c38a..9be4055b3 100644 --- a/src/shared/sysctl-util.c +++ b/src/shared/sysctl-util.c @@ -60,6 +60,22 @@ int sysctl_write(const char *property, const char *value) { return 0; } +int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value) { + const char *p; + + assert(IN_SET(af, AF_INET, AF_INET6)); + assert(property); + assert(value); + + p = strjoina("/proc/sys/net/ipv", af == AF_INET ? "4" : "6", + ifname ? "/conf/" : "", strempty(ifname), + property[0] == '/' ? "" : "/", property); + + log_debug("Setting '%s' to '%s'", p, value); + + return write_string_file(p, value, WRITE_STRING_FILE_VERIFY_ON_FAILURE | WRITE_STRING_FILE_DISABLE_BUFFER); +} + int sysctl_read(const char *property, char **content) { char *p; @@ -69,3 +85,25 @@ int sysctl_read(const char *property, char **content) { p = strjoina("/proc/sys/", property); return read_full_file(p, content, NULL); } + +int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret) { + _cleanup_free_ char *value = NULL; + const char *p; + int r; + + assert(IN_SET(af, AF_INET, AF_INET6)); + assert(property); + + p = strjoina("/proc/sys/net/ipv", af == AF_INET ? "4" : "6", + ifname ? "/conf/" : "", strempty(ifname), + property[0] == '/' ? "" : "/", property); + + r = read_one_line_file(p, &value); + if (r < 0) + return r; + + if (ret) + *ret = TAKE_PTR(value); + + return r; +} diff --git a/src/shared/sysctl-util.h b/src/shared/sysctl-util.h index fd7c78b2b..d50c6e438 100644 --- a/src/shared/sysctl-util.h +++ b/src/shared/sysctl-util.h @@ -1,7 +1,29 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #pragma once +#include +#include + +#include "macro.h" +#include "stdio-util.h" +#include "util.h" + char *sysctl_normalize(char *s); int sysctl_read(const char *property, char **value); int sysctl_write(const char *property, const char *value); +int sysctl_read_ip_property(int af, const char *ifname, const char *property, char **ret); +int sysctl_write_ip_property(int af, const char *ifname, const char *property, const char *value); +static inline int sysctl_write_ip_property_boolean(int af, const char *ifname, const char *property, bool value) { + return sysctl_write_ip_property(af, ifname, property, one_zero(value)); +} + +#define DEFINE_SYSCTL_WRITE_IP_PROPERTY(name, type, format) \ + static inline int sysctl_write_ip_property_##name(int af, const char *ifname, const char *property, type value) { \ + char buf[DECIMAL_STR_MAX(type)]; \ + xsprintf(buf, format, value); \ + return sysctl_write_ip_property(af, ifname, property, buf); \ + } + +DEFINE_SYSCTL_WRITE_IP_PROPERTY(int, int, "%i"); +DEFINE_SYSCTL_WRITE_IP_PROPERTY(uint32, uint32_t, "%" PRIu32); diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 4200032b3..4be9d7106 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -133,7 +133,7 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_ /* Wait until the device is initialized, so that we can get access to the ID_PATH property */ - r = sd_event_default(&event); + r = sd_event_new(&event); if (r < 0) return log_error_errno(r, "Failed to get default event: %m"); @@ -169,3 +169,26 @@ int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_ *ret = TAKE_PTR(data.device); return 0; } + +int device_is_renaming(sd_device *dev) { + int r; + + assert(dev); + + r = sd_device_get_property_value(dev, "ID_RENAMING", NULL); + if (r < 0 && r != -ENOENT) + return r; + + return r >= 0; +} + +bool device_for_action(sd_device *dev, DeviceAction action) { + DeviceAction a; + + assert(dev); + + if (device_get_action(dev, &a) < 0) + return false; + + return a == action; +} diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index 932c4a9cd..3c4544751 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -3,6 +3,7 @@ #include "sd-device.h" +#include "device-private.h" #include "time-util.h" typedef enum ResolveNameTiming { @@ -27,3 +28,5 @@ static inline int udev_parse_config(void) { } int device_wait_for_initialization(sd_device *device, const char *subsystem, sd_device **ret); +int device_is_renaming(sd_device *dev); +bool device_for_action(sd_device *dev, DeviceAction action); diff --git a/src/shared/uid-range.c b/src/shared/uid-range.c index 5fa7bd277..7cb7d8a47 100644 --- a/src/shared/uid-range.c +++ b/src/shared/uid-range.c @@ -6,9 +6,9 @@ #include "alloc-util.h" #include "macro.h" +#include "sort-util.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); diff --git a/src/shared/utmp-wtmp.c b/src/shared/utmp-wtmp.c index ef9427fa7..4b134b6c8 100644 --- a/src/shared/utmp-wtmp.c +++ b/src/shared/utmp-wtmp.c @@ -16,12 +16,12 @@ #include "fd-util.h" #include "hostname-util.h" #include "macro.h" +#include "memory-util.h" #include "path-util.h" #include "string-util.h" #include "terminal-util.h" #include "time-util.h" #include "user-util.h" -#include "util.h" #include "utmp-wtmp.h" int utmp_get_runlevel(int *runlevel, int *previous) { diff --git a/src/shared/volatile-util.c b/src/shared/volatile-util.c index 4d75bc0e9..5ca6ab337 100644 --- a/src/shared/volatile-util.c +++ b/src/shared/volatile-util.c @@ -12,33 +12,35 @@ int query_volatile_mode(VolatileMode *ret) { _cleanup_free_ char *mode = NULL; - VolatileMode m = VOLATILE_NO; int r; r = proc_cmdline_get_key("systemd.volatile", PROC_CMDLINE_VALUE_OPTIONAL, &mode); if (r < 0) return r; - if (r == 0) - goto finish; + if (r == 0) { + *ret = VOLATILE_NO; + return 0; + } if (mode) { + VolatileMode m; + m = volatile_mode_from_string(mode); if (m < 0) return -EINVAL; + + *ret = m; } else - m = VOLATILE_YES; + *ret = VOLATILE_YES; - r = 1; - -finish: - *ret = m; - return r; + return 1; } static const char* const volatile_mode_table[_VOLATILE_MODE_MAX] = { [VOLATILE_NO] = "no", [VOLATILE_YES] = "yes", [VOLATILE_STATE] = "state", + [VOLATILE_OVERLAY] = "overlay", }; DEFINE_STRING_TABLE_LOOKUP_WITH_BOOLEAN(volatile_mode, VolatileMode, VOLATILE_YES); diff --git a/src/shared/volatile-util.h b/src/shared/volatile-util.h index 8761c44ab..2d31bb117 100644 --- a/src/shared/volatile-util.h +++ b/src/shared/volatile-util.h @@ -5,6 +5,7 @@ typedef enum VolatileMode { VOLATILE_NO, VOLATILE_YES, VOLATILE_STATE, + VOLATILE_OVERLAY, _VOLATILE_MODE_MAX, _VOLATILE_MODE_INVALID = -1 } VolatileMode; diff --git a/src/shutdown/meson.build b/src/shutdown/meson.build new file mode 100644 index 000000000..ebf0bed24 --- /dev/null +++ b/src/shutdown/meson.build @@ -0,0 +1,5 @@ +systemd_shutdown_sources = files(''' + shutdown.c + umount.c + umount.h +'''.split()) diff --git a/src/core/shutdown.c b/src/shutdown/shutdown.c similarity index 97% rename from src/core/shutdown.c rename to src/shutdown/shutdown.c index cb47ee898..842ba57f1 100644 --- a/src/core/shutdown.c +++ b/src/shutdown/shutdown.c @@ -169,10 +169,10 @@ static int switch_root_initramfs(void) { * value input. For all other issues, report the failure and indicate that * the sync is not making progress. */ -static bool sync_making_progress(unsigned long long *prev_dirty) { +static int sync_making_progress(unsigned long long *prev_dirty) { _cleanup_fclose_ FILE *f = NULL; unsigned long long val = 0; - bool r = false; + int ret; f = fopen("/proc/meminfo", "re"); if (!f) @@ -205,11 +205,9 @@ static bool sync_making_progress(unsigned long long *prev_dirty) { val += ull; } - r = *prev_dirty > val; - + ret = *prev_dirty > val; *prev_dirty = val; - - return r; + return ret; } static void sync_with_progress(void) { @@ -243,7 +241,7 @@ static void sync_with_progress(void) { else if (r == -ETIMEDOUT) { /* Reset the check counter if the "Dirty" value is * decreasing */ - if (sync_making_progress(&dirty)) + if (sync_making_progress(&dirty) > 0) checks = 0; } else { log_error_errno(r, "Failed to sync filesystems and block devices: %m"); @@ -345,14 +343,14 @@ int main(int argc, char *argv[]) { bool changed = false; if (use_watchdog) - watchdog_ping(); + (void) watchdog_ping(); /* Let's trim the cgroup tree on each iteration so that we leave an empty cgroup tree around, so that container managers get a nice notify event when we are down */ if (cgroup) - cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); + (void) cg_trim(SYSTEMD_CGROUP_CONTROLLER, cgroup, false); if (need_umount) { log_info("Unmounting file systems."); @@ -442,7 +440,7 @@ 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, NULL); + (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); (void) rlimit_nofile_safe(); diff --git a/src/core/umount.c b/src/shutdown/umount.c similarity index 95% rename from src/core/umount.c rename to src/shutdown/umount.c index 7af0195aa..928bae6ab 100644 --- a/src/core/umount.c +++ b/src/shutdown/umount.c @@ -9,9 +9,9 @@ #include #include #include - -/* This needs to be after sys/mount.h :( */ -#include +#include +#include +#include #include "sd-device.h" @@ -22,6 +22,7 @@ #include "escape.h" #include "fd-util.h" #include "fstab-util.h" +#include "libmount-util.h" #include "linux-3.13/dm-ioctl.h" #include "mount-setup.h" #include "mount-util.h" @@ -35,9 +36,6 @@ #include "util.h" #include "virt.h" -DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_table*, mnt_free_table); -DEFINE_TRIVIAL_CLEANUP_FUNC(struct libmnt_iter*, mnt_free_iter); - static void mount_point_free(MountPoint **head, MountPoint *m) { assert(head); assert(m); @@ -76,11 +74,10 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) { struct libmnt_fs *fs; 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; bool try_remount_ro; - MountPoint *m; + _cleanup_free_ MountPoint *m = NULL; r = mnt_table_next_fs(t, i, &fs); if (r == 1) @@ -92,18 +89,15 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) { if (!path) continue; - if (cunescape(path, UNESCAPE_RELAX, &p) < 0) - return log_oom(); - 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. + * are turned into MS_RDONLY or the inversion of it. * * Even if there are duplicates later in mount_option_mangle() - * it shouldn't hurt anyways as they override each other. + * they shouldn't hurt anyways as they override each other. */ if (!strextend_with_separator(&options, ",", mnt_fs_get_vfs_options(fs), @@ -121,9 +115,9 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) { * and hence not worth spending time on. Also, in * unprivileged containers we might lack the rights to * unmount these things, hence don't bother. */ - if (mount_point_is_api(p) || - mount_point_ignore(p) || - PATH_STARTSWITH_SET(p, "/dev", "/sys", "/proc")) + if (mount_point_is_api(path) || + mount_point_ignore(path) || + PATH_STARTSWITH_SET(path, "/dev", "/sys", "/proc")) continue; /* If we are in a container, don't attempt to @@ -169,12 +163,15 @@ int mount_points_list_get(const char *mountinfo, MountPoint **head) { if (!m) return log_oom(); - free_and_replace(m->path, p); - free_and_replace(m->remount_options, remount_options); + m->path = strdup(path); + if (!m->path) + return log_oom(); + + m->remount_options = TAKE_PTR(remount_options); m->remount_flags = remount_flags; m->try_remount_ro = try_remount_ro; - LIST_PREPEND(mount_point, *head, m); + LIST_PREPEND(mount_point, *head, TAKE_PTR(m)); } return 0; @@ -198,10 +195,8 @@ int swap_list_get(const char *swaps, MountPoint **head) { for (;;) { struct libmnt_fs *fs; - - MountPoint *swap; + _cleanup_free_ MountPoint *swap = NULL; const char *source; - _cleanup_free_ char *d = NULL; r = mnt_table_next_fs(t, i, &fs); if (r == 1) @@ -213,16 +208,15 @@ int swap_list_get(const char *swaps, MountPoint **head) { if (!source) continue; - r = cunescape(source, UNESCAPE_RELAX, &d); - if (r < 0) - return r; - swap = new0(MountPoint, 1); if (!swap) return -ENOMEM; - free_and_replace(swap->path, d); - LIST_PREPEND(mount_point, *head, swap); + swap->path = strdup(source); + if (!swap->path) + return -ENOMEM; + + LIST_PREPEND(mount_point, *head, TAKE_PTR(swap)); } return 0; diff --git a/src/core/umount.h b/src/shutdown/umount.h similarity index 100% rename from src/core/umount.h rename to src/shutdown/umount.h diff --git a/src/sleep/sleep.c b/src/sleep/sleep.c index 5b7984a6f..11aabaf92 100644 --- a/src/sleep/sleep.c +++ b/src/sleep/sleep.c @@ -5,15 +5,20 @@ ***/ #include +#include #include #include #include +#include +#include +#include #include "sd-messages.h" #include "def.h" #include "exec-util.h" #include "fd-util.h" +#include "format-util.h" #include "fileio.h" #include "log.h" #include "main-func.h" @@ -166,7 +171,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, NULL); + (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); log_struct(LOG_INFO, "MESSAGE_ID=" SD_MESSAGE_SLEEP_START_STR, @@ -186,7 +191,7 @@ static int execute(char **modes, char **states) { "SLEEP=%s", arg_verb); arguments[1] = (char*) "post"; - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL); + (void) execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, arguments, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); return r; } diff --git a/src/socket-proxy/socket-proxyd.c b/src/socket-proxy/socket-proxyd.c index bac5c164d..abbbc9f2d 100644 --- a/src/socket-proxy/socket-proxyd.c +++ b/src/socket-proxy/socket-proxyd.c @@ -16,6 +16,7 @@ #include "sd-resolve.h" #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "log.h" #include "main-func.h" @@ -29,8 +30,8 @@ #include "util.h" #define BUFFER_SIZE (256 * 1024) -static unsigned arg_connections_max = 256; +static unsigned arg_connections_max = 256; static const char *arg_remote_host = NULL; typedef struct Context { @@ -141,7 +142,7 @@ static int connection_shovel( if (z > 0) { *full += z; shoveled = true; - } else if (z == 0 || IN_SET(errno, EPIPE, ECONNRESET)) { + } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) { *from_source = sd_event_source_unref(*from_source); *from = safe_close(*from); } else if (!IN_SET(errno, EAGAIN, EINTR)) @@ -153,7 +154,7 @@ static int connection_shovel( if (z > 0) { *full -= z; shoveled = true; - } else if (z == 0 || IN_SET(errno, EPIPE, ECONNRESET)) { + } else if (z == 0 || ERRNO_IS_DISCONNECT(errno)) { *to_source = sd_event_source_unref(*to_source); *to = safe_close(*to); } else if (!IN_SET(errno, EAGAIN, EINTR)) @@ -466,10 +467,10 @@ static int accept_cb(sd_event_source *s, int fd, uint32_t revents, void *userdat nfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK|SOCK_CLOEXEC); if (nfd < 0) { - if (errno != -EAGAIN) + if (!ERRNO_IS_ACCEPT_AGAIN(errno)) log_warning_errno(errno, "Failed to accept() socket: %m"); } else { - getpeername_pretty(nfd, true, &peer); + (void) getpeername_pretty(nfd, true, &peer); log_debug("New connection from %s", strna(peer)); r = add_connection_socket(context, nfd); diff --git a/src/stdio-bridge/stdio-bridge.c b/src/stdio-bridge/stdio-bridge.c index 7060897ab..52b9ce455 100644 --- a/src/stdio-bridge/stdio-bridge.c +++ b/src/stdio-bridge/stdio-bridge.c @@ -14,6 +14,7 @@ #include "build.h" #include "bus-internal.h" #include "bus-util.h" +#include "errno-util.h" #include "log.h" #include "main-func.h" #include "util.h" @@ -56,31 +57,28 @@ static int parse_argv(int argc, char *argv[]) { assert(argc >= 0); assert(argv); - while ((c = getopt_long(argc, argv, "hsup:", options, NULL)) >= 0) { + while ((c = getopt_long(argc, argv, "hp:M:", options, NULL)) >= 0) { switch (c) { case 'h': - help(); - return 0; + return help(); case ARG_VERSION: return version(); - case '?': - return -EINVAL; - case 'p': arg_bus_path = optarg; - break; case 'M': arg_bus_path = optarg; - arg_transport = BUS_TRANSPORT_MACHINE; - break; + + case '?': + return -EINVAL; + default: return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown option code %c", c); @@ -111,10 +109,8 @@ static int run(int argc, char *argv[]) { } else if (r == 1) { in_fd = SD_LISTEN_FDS_START; out_fd = SD_LISTEN_FDS_START; - } else { - log_error("Illegal number of file descriptors passed."); - return -EINVAL; - } + } else + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Illegal number of file descriptors passed."); is_unix = sd_is_socket(in_fd, AF_UNIX, 0, 0) > 0 && @@ -187,9 +183,13 @@ static int run(int argc, char *argv[]) { continue; r = sd_bus_process(b, &m); - if (r < 0) + if (r < 0) { /* treat 'connection reset by peer' as clean exit condition */ - return r == -ECONNRESET ? 0 : r; + if (ERRNO_IS_DISCONNECT(r)) + return 0; + + return log_error_errno(r, "Failed to process bus: %m"); + } if (m) { r = sd_bus_send(a, m, NULL); @@ -240,9 +240,10 @@ static int run(int argc, char *argv[]) { { struct pollfd p[3] = { - {.fd = fd, .events = events_a, }, - {.fd = STDIN_FILENO, .events = events_b & POLLIN, }, - {.fd = STDOUT_FILENO, .events = events_b & POLLOUT, }}; + {.fd = fd, .events = events_a }, + {.fd = STDIN_FILENO, .events = events_b & POLLIN }, + {.fd = STDOUT_FILENO, .events = events_b & POLLOUT }, + }; r = ppoll(p, ELEMENTSOF(p), ts, NULL); } diff --git a/src/sysctl/sysctl.c b/src/sysctl/sysctl.c index c67d79032..bb2643a48 100644 --- a/src/sysctl/sysctl.c +++ b/src/sysctl/sysctl.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include #include "conf-files.h" #include "def.h" @@ -21,7 +23,6 @@ #include "string-util.h" #include "strv.h" #include "sysctl-util.h" -#include "util.h" static char **arg_prefixes = NULL; static bool arg_cat_config = false; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 63dae2c87..92bbcfd27 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -23,8 +24,10 @@ #include "bus-common-errors.h" #include "bus-error.h" #include "bus-message.h" +#include "bus-unit-procs.h" #include "bus-unit-util.h" #include "bus-util.h" +#include "bus-wait-for-jobs.h" #include "cgroup-show.h" #include "cgroup-util.h" #include "copy.h" @@ -39,15 +42,18 @@ #include "glob-util.h" #include "hexdecoct.h" #include "hostname-util.h" +#include "in-addr-util.h" #include "initreq.h" #include "install.h" #include "io-util.h" +#include "journal-util.h" #include "list.h" #include "locale-util.h" #include "log.h" #include "logs-show.h" #include "macro.h" #include "main-func.h" +#include "memory-util.h" #include "mkdir.h" #include "pager.h" #include "parse-util.h" @@ -62,41 +68,24 @@ #include "sigbus.h" #include "signal-util.h" #include "socket-util.h" +#include "sort-util.h" #include "spawn-ask-password-agent.h" #include "spawn-polkit-agent.h" #include "special.h" #include "stat-util.h" #include "string-table.h" #include "strv.h" +#include "sysv-compat.h" #include "terminal-util.h" #include "tmpfile-util.h" #include "unit-def.h" #include "unit-name.h" #include "user-util.h" -#include "util.h" +#include "utf8.h" #include "utmp-wtmp.h" #include "verbs.h" #include "virt.h" -/* The init script exit status codes - 0 program is running or service is OK - 1 program is dead and /var/run pid file exists - 2 program is dead and /var/lock lock file exists - 3 program is not running - 4 program or service status is unknown - 5-99 reserved for future LSB use - 100-149 reserved for distribution use - 150-199 reserved for application use - 200-254 reserved -*/ -enum { - EXIT_PROGRAM_RUNNING_OR_SERVICE_OK = 0, - EXIT_PROGRAM_DEAD_AND_PID_EXISTS = 1, - EXIT_PROGRAM_DEAD_AND_LOCK_FILE_EXISTS = 2, - EXIT_PROGRAM_NOT_RUNNING = 3, - EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4, -}; - static char **arg_types = NULL; static char **arg_states = NULL; static char **arg_properties = NULL; @@ -125,6 +114,7 @@ static bool arg_dry_run = false; static bool arg_quiet = false; static bool arg_full = false; static bool arg_recursive = false; +static bool arg_show_transaction = false; static int arg_force = 0; static bool arg_ask_password = false; static bool arg_runtime = false; @@ -134,8 +124,6 @@ static const char *arg_kill_who = NULL; static int arg_signal = SIGTERM; static char *arg_root = NULL; static usec_t arg_when = 0; -static char *arg_esp_path = NULL; -static char *argv_cmdline = NULL; static enum action { ACTION_SYSTEMCTL, ACTION_HALT, @@ -167,10 +155,18 @@ static unsigned arg_lines = 10; static OutputMode arg_output = OUTPUT_SHORT; static bool arg_plain = false; static bool arg_firmware_setup = false; +static usec_t arg_boot_loader_menu = USEC_INFINITY; +static const char *arg_boot_loader_entry = NULL; static bool arg_now = false; static bool arg_jobs_before = false; static bool arg_jobs_after = false; +STATIC_DESTRUCTOR_REGISTER(arg_wall, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_root, freep); +STATIC_DESTRUCTOR_REGISTER(arg_types, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_states, strv_freep); +STATIC_DESTRUCTOR_REGISTER(arg_properties, strv_freep); + static int daemon_reload(int argc, char *argv[], void* userdata); static int trivial_method(int argc, char *argv[], void *userdata); static int halt_now(enum action a); @@ -733,7 +729,6 @@ static int get_unit_list_recursive( *_machines = NULL; *_unit_infos = TAKE_PTR(unit_infos); - *_replies = TAKE_PTR(replies); return c; @@ -2659,10 +2654,8 @@ static int get_state_one_unit(sd_bus *bus, const char *name, UnitActiveState *ac return log_error_errno(r, "Failed to retrieve unit state: %s", bus_error_message(&error, r)); state = unit_active_state_from_string(buf); - if (state == _UNIT_ACTIVE_STATE_INVALID) { - log_error("Invalid unit state '%s' for: %s", buf, name); - return -EINVAL; - } + if (state < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid unit state '%s' for: %s", buf, name); *active_state = state; return 0; @@ -2748,25 +2741,26 @@ static int check_triggering_units(sd_bus *bus, const char *name) { } static const struct { - const char *verb; - const char *method; + const char *verb; /* systemctl verb */ + const char *method; /* Name of the specific D-Bus method */ + const char *job_type; /* Job type when passing to the generic EnqueueUnitJob() method */ } unit_actions[] = { - { "start", "StartUnit" }, - { "stop", "StopUnit" }, - { "condstop", "StopUnit" }, - { "reload", "ReloadUnit" }, - { "restart", "RestartUnit" }, - { "try-restart", "TryRestartUnit" }, - { "condrestart", "TryRestartUnit" }, - { "reload-or-restart", "ReloadOrRestartUnit" }, - { "try-reload-or-restart", "ReloadOrTryRestartUnit" }, - { "reload-or-try-restart", "ReloadOrTryRestartUnit" }, - { "condreload", "ReloadOrTryRestartUnit" }, - { "force-reload", "ReloadOrTryRestartUnit" } + { "start", "StartUnit", "start" }, + { "stop", "StopUnit", "stop" }, + { "condstop", "StopUnit", "stop" }, /* legacy alias */ + { "reload", "ReloadUnit", "reload" }, + { "restart", "RestartUnit", "restart" }, + { "try-restart", "TryRestartUnit", "try-restart" }, + { "condrestart", "TryRestartUnit", "try-restart" }, /* legacy alias */ + { "reload-or-restart", "ReloadOrRestartUnit", "reload-or-restart" }, + { "try-reload-or-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, + { "reload-or-try-restart", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */ + { "condreload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */ + { "force-reload", "ReloadOrTryRestartUnit", "reload-or-try-restart" }, /* legacy alias */ }; static const char *verb_to_method(const char *verb) { - uint i; + size_t i; for (i = 0; i < ELEMENTSOF(unit_actions); i++) if (streq_ptr(unit_actions[i].verb, verb)) @@ -2775,14 +2769,14 @@ static const char *verb_to_method(const char *verb) { return "StartUnit"; } -static const char *method_to_verb(const char *method) { - uint i; +static const char *verb_to_job_type(const char *verb) { + size_t i; for (i = 0; i < ELEMENTSOF(unit_actions); i++) - if (streq_ptr(unit_actions[i].method, method)) - return unit_actions[i].verb; + if (streq_ptr(unit_actions[i].verb, verb)) + return unit_actions[i].job_type; - return "n/a"; + return "start"; } typedef struct { @@ -2887,9 +2881,60 @@ static int on_properties_changed(sd_bus_message *m, void *userdata, sd_bus_error return 0; } +static int wait_context_watch( + WaitContext *wait_context, + sd_bus *bus, + const char *name) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char *unit_path = NULL; + int r; + + assert(wait_context); + assert(name); + + log_debug("Watching for property changes of %s", name); + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "RefUnit", + &error, + NULL, + "s", name); + if (r < 0) + return log_error_errno(r, "Failed to add reference to unit %s: %s", name, bus_error_message(&error, r)); + + unit_path = unit_dbus_path_from_name(name); + if (!unit_path) + return log_oom(); + + r = set_ensure_allocated(&wait_context->unit_paths, &string_hash_ops); + if (r < 0) + return log_oom(); + + r = set_put_strdup(wait_context->unit_paths, unit_path); + if (r < 0) + return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path); + + r = sd_bus_match_signal_async(bus, + &wait_context->match, + NULL, + unit_path, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + on_properties_changed, NULL, wait_context); + if (r < 0) + return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m"); + + return 0; +} + static int start_unit_one( sd_bus *bus, - const char *method, + const char *method, /* When using classic per-job bus methods */ + const char *job_type, /* When using new-style EnqueueUnitJob() */ const char *name, const char *mode, sd_bus_error *error, @@ -2898,6 +2943,7 @@ static int start_unit_one( _cleanup_(sd_bus_message_unrefp) sd_bus_message *reply = NULL; const char *path; + bool done = false; int r; assert(method); @@ -2906,81 +2952,88 @@ static int start_unit_one( assert(error); if (wait_context) { - _cleanup_free_ char *unit_path = NULL; - - log_debug("Watching for property changes of %s", name); - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - "RefUnit", - error, - NULL, - "s", name); + r = wait_context_watch(wait_context, bus, name); if (r < 0) - return log_error_errno(r, "Failed to RefUnit %s: %s", name, bus_error_message(error, r)); - - unit_path = unit_dbus_path_from_name(name); - if (!unit_path) - return log_oom(); - - r = set_put_strdup(wait_context->unit_paths, unit_path); - if (r < 0) - return log_error_errno(r, "Failed to add unit path %s to set: %m", unit_path); - - r = sd_bus_match_signal_async(bus, - &wait_context->match, - NULL, - unit_path, - "org.freedesktop.DBus.Properties", - "PropertiesChanged", - on_properties_changed, NULL, wait_context); - if (r < 0) - return log_error_errno(r, "Failed to request match for PropertiesChanged signal: %m"); + return r; } log_debug("%s dbus call org.freedesktop.systemd1.Manager %s(%s, %s)", arg_dry_run ? "Would execute" : "Executing", method, name, mode); + if (arg_dry_run) return 0; - r = sd_bus_call_method( - bus, - "org.freedesktop.systemd1", - "/org/freedesktop/systemd1", - "org.freedesktop.systemd1.Manager", - method, - error, - &reply, - "ss", name, mode); - if (r < 0) { - const char *verb; + if (arg_show_transaction) { + _cleanup_(sd_bus_error_free) sd_bus_error enqueue_error = SD_BUS_ERROR_NULL; - /* There's always a fallback possible for legacy actions. */ - if (arg_action != ACTION_SYSTEMCTL) - return r; + /* Use the new, fancy EnqueueUnitJob() API if the user wants us to print the transaction */ + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "EnqueueUnitJob", + &enqueue_error, + &reply, + "sss", + name, job_type, mode); + if (r < 0) { + if (!sd_bus_error_has_name(&enqueue_error, SD_BUS_ERROR_UNKNOWN_METHOD)) { + (void) sd_bus_error_move(error, &enqueue_error); + goto fail; + } - verb = method_to_verb(method); + /* Hmm, the API is not yet available. Let's use the classic API instead (see below). */ + log_notice("--show-transaction not supported by this service manager, proceeding without."); + } else { + const char *u, *jt; + uint32_t id; - log_error("Failed to %s %s: %s", verb, name, bus_error_message(error, r)); + r = sd_bus_message_read(reply, "uosos", &id, &path, &u, NULL, &jt); + if (r < 0) + return bus_log_parse_error(r); - if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) && - !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED) && - !sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE)) - log_error("See %s logs and 'systemctl%s status%s %s' for details.", - arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", - arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", - name[0] == '-' ? " --" : "", - name); + log_info("Enqueued anchor job %" PRIu32 " %s/%s.", id, u, jt); - return r; + r = sd_bus_message_enter_container(reply, 'a', "(uosos)"); + if (r < 0) + return bus_log_parse_error(r); + for (;;) { + r = sd_bus_message_read(reply, "(uosos)", &id, NULL, &u, NULL, &jt); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + log_info("Enqueued auxiliary job %" PRIu32 " %s/%s.", id, u, jt); + } + + r = sd_bus_message_exit_container(reply); + if (r < 0) + return bus_log_parse_error(r); + + done = true; + } } - r = sd_bus_message_read(reply, "o", &path); - if (r < 0) - return bus_log_parse_error(r); + if (!done) { + r = sd_bus_call_method( + bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + method, + error, + &reply, + "ss", name, mode); + if (r < 0) + goto fail; + + r = sd_bus_message_read(reply, "o", &path); + if (r < 0) + return bus_log_parse_error(r); + } if (need_daemon_reload(bus, name) > 0) warn_unit_file_changed(name); @@ -2989,10 +3042,28 @@ static int start_unit_one( log_debug("Adding %s to the set", path); r = bus_wait_for_jobs_add(w, path); if (r < 0) - return log_oom(); + return log_error_errno(r, "Failed to watch job for %s: %m", name); } return 0; + +fail: + /* There's always a fallback possible for legacy actions. */ + if (arg_action != ACTION_SYSTEMCTL) + return r; + + log_error_errno(r, "Failed to %s %s: %s", job_type, name, bus_error_message(error, r)); + + if (!sd_bus_error_has_name(error, BUS_ERROR_NO_SUCH_UNIT) && + !sd_bus_error_has_name(error, BUS_ERROR_UNIT_MASKED) && + !sd_bus_error_has_name(error, BUS_ERROR_JOB_TYPE_NOT_APPLICABLE)) + log_error("See %s logs and 'systemctl%s status%s %s' for details.", + arg_scope == UNIT_FILE_SYSTEM ? "system" : "user", + arg_scope == UNIT_FILE_SYSTEM ? "" : " --user", + name[0] == '-' ? " --" : "", + name); + + return r; } static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***ret) { @@ -3049,7 +3120,6 @@ static int expand_names(sd_bus *bus, char **names, const char* suffix, char ***r } *ret = TAKE_PTR(mangled); - return 0; } @@ -3058,21 +3128,21 @@ static const struct { const char *verb; const char *mode; } action_table[_ACTION_MAX] = { - [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, - [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, - [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, - [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, - [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, - [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, - [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, - [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, - [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, - [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, - [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, - [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, - [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, + [ACTION_HALT] = { SPECIAL_HALT_TARGET, "halt", "replace-irreversibly" }, + [ACTION_POWEROFF] = { SPECIAL_POWEROFF_TARGET, "poweroff", "replace-irreversibly" }, + [ACTION_REBOOT] = { SPECIAL_REBOOT_TARGET, "reboot", "replace-irreversibly" }, + [ACTION_KEXEC] = { SPECIAL_KEXEC_TARGET, "kexec", "replace-irreversibly" }, + [ACTION_RUNLEVEL2] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL3] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL4] = { SPECIAL_MULTI_USER_TARGET, NULL, "isolate" }, + [ACTION_RUNLEVEL5] = { SPECIAL_GRAPHICAL_TARGET, NULL, "isolate" }, + [ACTION_RESCUE] = { SPECIAL_RESCUE_TARGET, "rescue", "isolate" }, + [ACTION_EMERGENCY] = { SPECIAL_EMERGENCY_TARGET, "emergency", "isolate" }, + [ACTION_DEFAULT] = { SPECIAL_DEFAULT_TARGET, "default", "isolate" }, + [ACTION_EXIT] = { SPECIAL_EXIT_TARGET, "exit", "replace-irreversibly" }, + [ACTION_SUSPEND] = { SPECIAL_SUSPEND_TARGET, "suspend", "replace-irreversibly" }, + [ACTION_HIBERNATE] = { SPECIAL_HIBERNATE_TARGET, "hibernate", "replace-irreversibly" }, + [ACTION_HYBRID_SLEEP] = { SPECIAL_HYBRID_SLEEP_TARGET, "hybrid-sleep", "replace-irreversibly" }, [ACTION_SUSPEND_THEN_HIBERNATE] = { SPECIAL_SUSPEND_THEN_HIBERNATE_TARGET, "suspend-then-hibernate", "replace-irreversibly" }, }; @@ -3086,20 +3156,38 @@ static enum action verb_to_action(const char *verb) { return _ACTION_INVALID; } +static const char** make_extra_args(const char *extra_args[static 4]) { + size_t n = 0; + + if (arg_scope != UNIT_FILE_SYSTEM) + extra_args[n++] = "--user"; + + if (arg_transport == BUS_TRANSPORT_REMOTE) { + extra_args[n++] = "-H"; + extra_args[n++] = arg_host; + } else if (arg_transport == BUS_TRANSPORT_MACHINE) { + extra_args[n++] = "-M"; + extra_args[n++] = arg_host; + } else + assert(arg_transport == BUS_TRANSPORT_LOCAL); + + extra_args[n] = NULL; + return extra_args; +} + 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; + const char *method, *job_type, *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; char **name; - if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) { - log_error("--wait may only be used with the 'start' or 'restart' commands."); - return -EINVAL; - } + if (arg_wait && !STR_IN_SET(argv[0], "start", "restart")) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "--wait may only be used with the 'start' or 'restart' commands."); /* we cannot do sender tracking on the private bus, so we need the full * one for RefUnit to implement --wait */ @@ -3116,27 +3204,34 @@ static int start_unit(int argc, char *argv[], void *userdata) { action = verb_to_action(argv[0]); if (action != _ACTION_INVALID) { + /* A command in style "systemctl reboot", "systemctl poweroff", … */ method = "StartUnit"; + job_type = "start"; mode = action_table[action].mode; one_name = action_table[action].target; } else { if (streq(argv[0], "isolate")) { + /* A "systemctl isolate …" command */ method = "StartUnit"; + job_type = "start"; mode = "isolate"; - suffix = ".target"; } else { + /* A command in style of "systemctl start …", "sysemctl stop …" and so on */ method = verb_to_method(argv[0]); + job_type = verb_to_job_type(argv[0]); mode = arg_job_mode; } one_name = NULL; } } else { + /* A SysV legacy command such as "halt", "reboot", "poweroff", … */ assert(arg_action >= 0 && arg_action < _ACTION_MAX); assert(action_table[arg_action].target); assert(action_table[arg_action].mode); method = "StartUnit"; + job_type = "start"; mode = action_table[arg_action].mode; one_name = action_table[arg_action].target; } @@ -3158,10 +3253,6 @@ static int start_unit(int argc, char *argv[], void *userdata) { } if (arg_wait) { - wait_context.unit_paths = set_new(&string_hash_ops); - if (!wait_context.unit_paths) - return log_oom(); - r = sd_bus_call_method_async( bus, NULL, @@ -3173,9 +3264,11 @@ static int start_unit(int argc, char *argv[], void *userdata) { NULL); if (r < 0) return log_error_errno(r, "Failed to enable subscription: %m"); + r = sd_event_default(&wait_context.event); if (r < 0) return log_error_errno(r, "Failed to allocate event loop: %m"); + r = sd_bus_attach_event(bus, wait_context.event, 0); if (r < 0) return log_error_errno(r, "Failed to attach bus to event loop: %m"); @@ -3184,7 +3277,7 @@ 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; - r = start_unit_one(bus, method, *name, mode, &error, w, arg_wait ? &wait_context : NULL); + r = start_unit_one(bus, method, job_type, *name, mode, &error, w, arg_wait ? &wait_context : NULL); if (ret == EXIT_SUCCESS && r < 0) ret = translate_bus_error_to_exit_status(r, &error); @@ -3196,22 +3289,9 @@ static int start_unit(int argc, char *argv[], void *userdata) { } if (!arg_no_block) { - const char* extra_args[4] = {}; - int arg_count = 0; + const char* extra_args[4]; - if (arg_scope != UNIT_FILE_SYSTEM) - extra_args[arg_count++] = "--user"; - - assert(IN_SET(arg_transport, BUS_TRANSPORT_LOCAL, BUS_TRANSPORT_REMOTE, BUS_TRANSPORT_MACHINE)); - if (arg_transport == BUS_TRANSPORT_REMOTE) { - extra_args[arg_count++] = "-H"; - extra_args[arg_count++] = arg_host; - } else if (arg_transport == BUS_TRANSPORT_MACHINE) { - extra_args[arg_count++] = "-M"; - extra_args[arg_count++] = arg_host; - } - - r = bus_wait_for_jobs(w, arg_quiet, extra_args); + r = bus_wait_for_jobs(w, arg_quiet, make_extra_args(extra_args)); if (r < 0) return r; @@ -3270,64 +3350,38 @@ static int logind_set_wall_message(void) { } #endif -/* Ask systemd-logind, which might grant access to unprivileged users - * through polkit */ +/* Ask systemd-logind, which might grant access to unprivileged users through polkit */ static int logind_reboot(enum action a) { #if ENABLE_LOGIND + static const struct { + const char *method; + const char *description; + } actions[_ACTION_MAX] = { + [ACTION_POWEROFF] = { "PowerOff", "power off system" }, + [ACTION_REBOOT] = { "Reboot", "reboot system" }, + [ACTION_HALT] = { "Halt", "halt system" }, + [ACTION_SUSPEND] = { "Suspend", "suspend system" }, + [ACTION_HIBERNATE] = { "Hibernate", "hibernate system" }, + [ACTION_HYBRID_SLEEP] = { "HybridSleep", "put system into hybrid sleep" }, + [ACTION_SUSPEND_THEN_HIBERNATE] = { "SuspendThenHibernate", "suspend system, hibernate later" }, + }; + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; - const char *method, *description; sd_bus *bus; int r; + if (a < 0 || a >= _ACTION_MAX || !actions[a].method) + return -EINVAL; + r = acquire_bus(BUS_FULL, &bus); if (r < 0) return r; - switch (a) { - - case ACTION_POWEROFF: - method = "PowerOff"; - description = "power off system"; - break; - - case ACTION_REBOOT: - method = "Reboot"; - description = "reboot system"; - break; - - case ACTION_HALT: - method = "Halt"; - description = "halt system"; - break; - - case ACTION_SUSPEND: - method = "Suspend"; - description = "suspend system"; - break; - - case ACTION_HIBERNATE: - method = "Hibernate"; - description = "hibernate system"; - break; - - case ACTION_HYBRID_SLEEP: - method = "HybridSleep"; - description = "put system into hybrid sleep"; - break; - - case ACTION_SUSPEND_THEN_HIBERNATE: - method = "SuspendThenHibernate"; - description = "put system into suspend followed by hibernate"; - break; - - default: - return -EINVAL; - } - polkit_agent_open_maybe(); (void) logind_set_wall_message(); - log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", method); + log_debug("%s org.freedesktop.login1.Manager %s dbus call.", arg_dry_run ? "Would execute" : "Executing", actions[a].method); + if (arg_dry_run) return 0; @@ -3336,12 +3390,12 @@ static int logind_reboot(enum action a) { "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", - method, + actions[a].method, &error, NULL, "b", arg_ask_password); if (r < 0) - return log_error_errno(r, "Failed to %s via logind: %s", description, bus_error_message(&error, r)); + return log_error_errno(r, "Failed to %s via logind: %s", actions[a].description, bus_error_message(&error, r)); return 0; #else @@ -3407,10 +3461,8 @@ static int logind_check_inhibitors(enum action a) { if (!sv) return log_oom(); - if (!pid_is_valid((pid_t) pid)) { - log_error("Invalid PID "PID_FMT".", (pid_t) pid); - return -ERANGE; - } + if (!pid_is_valid((pid_t) pid)) + return log_error_errno(SYNTHETIC_ERRNO(ERANGE), "Invalid PID "PID_FMT".", (pid_t) pid); if (!strv_contains(sv, IN_SET(a, @@ -3470,7 +3522,11 @@ static int logind_check_inhibitors(enum action a) { #endif } -static int logind_prepare_firmware_setup(void) { +static int prepare_firmware_setup(void) { + + if (!arg_firmware_setup) + return 0; + #if ENABLE_LOGIND _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; sd_bus *bus; @@ -3494,32 +3550,80 @@ static int logind_prepare_firmware_setup(void) { return 0; #else - log_error("Cannot remotely indicate to EFI to boot into setup mode."); - return -ENOSYS; + return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), + "Booting into firmware setup not supported."); #endif } -static int prepare_firmware_setup(void) { - int r; +static int prepare_boot_loader_menu(void) { - if (!arg_firmware_setup) + if (arg_boot_loader_menu == USEC_INFINITY) return 0; - if (arg_transport == BUS_TRANSPORT_LOCAL) { +#if ENABLE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; - r = efi_set_reboot_to_firmware(true); - if (r < 0) - log_debug_errno(r, "Cannot indicate to EFI to boot into setup mode, will retry via logind: %m"); - else - return r; - } + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; - return logind_prepare_firmware_setup(); + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToBootLoaderMenu", + &error, + NULL, + "t", arg_boot_loader_menu); + if (r < 0) + return log_error_errno(r, "Cannot indicate to boot loader to enter boot loader entry menu: %s", bus_error_message(&error, r)); + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), + "Booting into boot loader menu not supported."); +#endif +} + +static int prepare_boot_loader_entry(void) { + + if (!arg_boot_loader_entry) + return 0; + +#if ENABLE_LOGIND + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus *bus; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_call_method( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "SetRebootToBootLoaderEntry", + &error, + NULL, + "s", arg_boot_loader_entry); + if (r < 0) + return log_error_errno(r, "Cannot set boot into loader entry '%s': %s", arg_boot_loader_entry, bus_error_message(&error, r)); + + return 0; +#else + return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), + "Booting into boot loader entry not supported."); +#endif } static int load_kexec_kernel(void) { _cleanup_(boot_config_free) BootConfig config = {}; - _cleanup_free_ char *where = NULL, *kernel = NULL, *initrd = NULL, *options = NULL; + _cleanup_free_ char *kernel = NULL, *initrd = NULL, *options = NULL; const BootEntry *e; pid_t pid; int r; @@ -3532,21 +3636,36 @@ static int load_kexec_kernel(void) { if (access(KEXEC, X_OK) < 0) return log_error_errno(errno, KEXEC" is not available: %m"); - r = find_default_boot_entry(arg_esp_path, &where, &config, &e); - if (r == -ENOKEY) /* find_default_boot_entry() doesn't warn about this case */ + r = boot_entries_load_config_auto(NULL, NULL, &config); + if (r == -ENOKEY) /* The call doesn't log about ENOKEY, let's do so here. */ 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; - if (strv_length(e->initrd) > 1) { - log_error("Boot entry specifies multiple initrds, which is not supported currently."); - return -EINVAL; + e = boot_config_default_entry(&config); + if (!e) + return log_error_errno(SYNTHETIC_ERRNO(ENOENT), + "No boot loader entry suitable as default, refusing to guess."); + + log_debug("Found default boot loader entry in file \"%s\"", e->path); + + if (!e->kernel) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Boot entry does not refer to Linux kernel, which is not supported currently."); + if (strv_length(e->initrd) > 1) + return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), + "Boot entry specifies multiple initrds, which is not supported currently."); + + kernel = path_join(e->root, e->kernel); + if (!kernel) + return log_oom(); + + if (!strv_isempty(e->initrd)) { + initrd = path_join(e->root, e->initrd[0]); + if (!initrd) + return log_oom(); } - kernel = path_join(where, e->kernel); - if (!strv_isempty(e->initrd)) - initrd = path_join(where, *e->initrd); options = strv_join(e->options, " "); if (!options) return log_oom(); @@ -3560,7 +3679,7 @@ static int load_kexec_kernel(void) { if (arg_dry_run) return 0; - r = safe_fork("(kexec)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); + r = safe_fork("(kexec)", FORK_WAIT|FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_RLIMIT_NOFILE_SAFE|FORK_LOG, &pid); if (r < 0) return r; if (r == 0) { @@ -3569,19 +3688,14 @@ static int load_kexec_kernel(void) { "--load", kernel, "--append", options, initrd ? "--initrd" : NULL, initrd, - NULL }; + NULL + }; /* Child */ execv(args[0], (char * const *) args); _exit(EXIT_FAILURE); } - r = wait_for_terminate_and_check("kexec", pid, WAIT_LOG); - if (r < 0) - return r; - if (r > 0) - /* Command failed */ - return -EPROTO; return 0; } @@ -3633,8 +3747,16 @@ static int start_special(int argc, char *argv[], void *userdata) { if (r < 0) return r; + r = prepare_boot_loader_menu(); + if (r < 0) + return r; + + r = prepare_boot_loader_entry(); + if (r < 0) + return r; + if (a == ACTION_REBOOT && argc > 1) { - r = update_reboot_parameter_and_warn(argv[1]); + r = update_reboot_parameter_and_warn(argv[1], false); if (r < 0) return r; @@ -4553,21 +4675,23 @@ static int map_conditions(sd_bus *bus, const char *member, sd_bus_message *m, sd while ((r = sd_bus_message_read(m, "(sbbsi)", &cond, &trigger, &negate, ¶m, &state)) > 0) { _cleanup_(unit_condition_freep) UnitCondition *c = NULL; - c = new0(UnitCondition, 1); + c = new(UnitCondition, 1); if (!c) return -ENOMEM; - c->name = strdup(cond); - c->param = strdup(param); + *c = (UnitCondition) { + .name = strdup(cond), + .param = strdup(param), + .trigger = trigger, + .negate = negate, + .tristate = state, + }; + if (!c->name || !c->param) return -ENOMEM; - c->trigger = trigger; - c->negate = negate; - c->tristate = state; - LIST_PREPEND(conditions, i->conditions, c); - c = NULL; + LIST_PREPEND(conditions, i->conditions, TAKE_PTR(c)); } if (r < 0) return r; @@ -4664,6 +4788,23 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m switch (bus_type) { + case SD_BUS_TYPE_INT32: + if (endswith(name, "ActionExitStatus")) { + int32_t i; + + r = sd_bus_message_read_basic(m, bus_type, &i); + if (r < 0) + return r; + + if (i >= 0 && i <= 255) + bus_print_property_valuef(name, expected_value, value, "%"PRIi32, i); + else if (all) + bus_print_property_value(name, expected_value, value, "[not set]"); + + return 1; + } + break; + case SD_BUS_TYPE_STRUCT: if (contents[0] == SD_BUS_TYPE_UINT32 && streq(name, "Job")) { @@ -4674,9 +4815,9 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); if (u > 0) - bus_print_property_value(name, expected_value, value, "%"PRIu32, u); + bus_print_property_valuef(name, expected_value, value, "%"PRIu32, u); else if (all) - bus_print_property_value(name, expected_value, value, "%s", ""); + bus_print_property_value(name, expected_value, value, ""); return 1; @@ -4688,7 +4829,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); if (all || !isempty(s)) - bus_print_property_value(name, expected_value, value, "%s", s); + bus_print_property_value(name, expected_value, value, s); return 1; @@ -4699,11 +4840,14 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m if (r < 0) return bus_log_parse_error(r); - if (all || !isempty(a) || !isempty(b)) - bus_print_property_value(name, expected_value, value, "%s \"%s\"", strempty(a), strempty(b)); + if (!isempty(a) || !isempty(b)) + bus_print_property_valuef(name, expected_value, value, "%s \"%s\"", strempty(a), strempty(b)); + else if (all) + bus_print_property_value(name, expected_value, value, ""); return 1; - } else if (streq_ptr(name, "SystemCallFilter")) { + + } else if (STR_IN_SET(name, "SystemCallFilter", "RestrictAddressFamilies")) { _cleanup_strv_free_ char **l = NULL; int whitelist; @@ -4747,6 +4891,83 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m } return 1; + + } else if (STR_IN_SET(name, "SELinuxContext", "AppArmorProfile", "SmackProcessLabel")) { + int ignore; + const char *s; + + r = sd_bus_message_read(m, "(bs)", &ignore, &s); + if (r < 0) + return bus_log_parse_error(r); + + if (!isempty(s)) + bus_print_property_valuef(name, expected_value, value, "%s%s", ignore ? "-" : "", s); + else if (all) + bus_print_property_value(name, expected_value, value, ""); + + return 1; + + } else if (endswith(name, "ExitStatus") && streq(contents, "aiai")) { + const int32_t *status, *signal; + size_t sz_status, sz_signal, i; + + r = sd_bus_message_enter_container(m, 'r', "aiai"); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(m, 'i', (const void **) &status, &sz_status); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(m, 'i', (const void **) &signal, &sz_signal); + 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); + + sz_status /= sizeof(int32_t); + sz_signal /= sizeof(int32_t); + + if (all || sz_status > 0 || sz_signal > 0) { + bool first = true; + + if (!value) { + fputs(name, stdout); + fputc('=', stdout); + } + + for (i = 0; i < sz_status; i++) { + if (status[i] < 0 || status[i] > 255) + continue; + + if (first) + first = false; + else + fputc(' ', stdout); + + printf("%"PRIi32, status[i]); + } + + for (i = 0; i < sz_signal; i++) { + const char *str; + + str = signal_to_string((int) signal[i]); + if (!str) + continue; + + if (first) + first = false; + else + fputc(' ', stdout); + + fputs(str, stdout); + } + + fputc('\n', stdout); + } + return 1; } break; @@ -4762,7 +4983,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(sb)", &path, &ignore)) > 0) - bus_print_property_value(name, expected_value, value, "%s (ignore_errors=%s)", path, yes_no(ignore)); + bus_print_property_valuef(name, expected_value, value, "%s (ignore_errors=%s)", path, yes_no(ignore)); if (r < 0) return bus_log_parse_error(r); @@ -4781,7 +5002,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) - bus_print_property_value(name, expected_value, value, "%s (%s)", path, type); + bus_print_property_valuef(name, expected_value, value, "%s (%s)", path, type); if (r < 0) return bus_log_parse_error(r); @@ -4799,7 +5020,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(ss)", &type, &path)) > 0) - bus_print_property_value(name, expected_value, value, "%s (%s)", path, type); + bus_print_property_valuef(name, expected_value, value, "%s (%s)", path, type); if (r < 0) return bus_log_parse_error(r); @@ -4820,9 +5041,9 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m while ((r = sd_bus_message_read(m, "(stt)", &base, &v, &next_elapse)) > 0) { char timespan1[FORMAT_TIMESPAN_MAX], timespan2[FORMAT_TIMESPAN_MAX]; - 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)); + bus_print_property_valuef(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); @@ -4844,8 +5065,8 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m while ((r = sd_bus_message_read(m, "(sst)", &base, &spec, &next_elapse)) > 0) { char timestamp[FORMAT_TIMESTAMP_MAX]; - bus_print_property_value(name, expected_value, value, "{ %s=%s ; next_elapse=%s }", base, spec, - format_timestamp(timestamp, sizeof(timestamp), next_elapse)); + bus_print_property_valuef(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); @@ -4869,18 +5090,18 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m tt = strv_join(info.argv, " "); - 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))); + bus_print_property_valuef(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); @@ -4901,7 +5122,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(ss)", &path, &rwm)) > 0) - bus_print_property_value(name, expected_value, value, "%s %s", strna(path), strna(rwm)); + bus_print_property_valuef(name, expected_value, value, "%s %s", strna(path), strna(rwm)); if (r < 0) return bus_log_parse_error(r); @@ -4921,7 +5142,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(st)", &path, &weight)) > 0) - bus_print_property_value(name, expected_value, value, "%s %"PRIu64, strna(path), weight); + bus_print_property_valuef(name, expected_value, value, "%s %"PRIu64, strna(path), weight); if (r < 0) return bus_log_parse_error(r); @@ -4942,7 +5163,7 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m return bus_log_parse_error(r); while ((r = sd_bus_message_read(m, "(st)", &path, &bandwidth)) > 0) - bus_print_property_value(name, expected_value, value, "%s %"PRIu64, strna(path), bandwidth); + bus_print_property_valuef(name, expected_value, value, "%s %"PRIu64, strna(path), bandwidth); if (r < 0) return bus_log_parse_error(r); @@ -4963,8 +5184,8 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m 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)); + bus_print_property_valuef(name, expected_value, value, "%s %s", strna(path), + format_timespan(ts, sizeof(ts), target, 1)); if (r < 0) return bus_log_parse_error(r); @@ -4988,7 +5209,187 @@ static int print_property(const char *name, const char *expected_value, sd_bus_m if (n < 0) return log_oom(); - bus_print_property_value(name, expected_value, value, "%s", h); + bus_print_property_value(name, expected_value, value, h); + + return 1; + + } else if (STR_IN_SET(name, "IPAddressAllow", "IPAddressDeny")) { + _cleanup_free_ char *addresses = NULL; + + r = sd_bus_message_enter_container(m, 'a', "(iayu)"); + if (r < 0) + return bus_log_parse_error(r); + + for (;;) { + _cleanup_free_ char *str = NULL; + uint32_t prefixlen; + int32_t family; + const void *ap; + size_t an; + + r = sd_bus_message_enter_container(m, 'r', "iayu"); + if (r < 0) + return bus_log_parse_error(r); + if (r == 0) + break; + + r = sd_bus_message_read(m, "i", &family); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read_array(m, 'y', &ap, &an); + if (r < 0) + return bus_log_parse_error(r); + + r = sd_bus_message_read(m, "u", &prefixlen); + 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 (!IN_SET(family, AF_INET, AF_INET6)) + continue; + + if (an != FAMILY_ADDRESS_SIZE(family)) + continue; + + if (prefixlen > FAMILY_ADDRESS_SIZE(family) * 8) + continue; + + if (in_addr_prefix_to_string(family, (union in_addr_union *) ap, prefixlen, &str) < 0) + continue; + + if (!strextend_with_separator(&addresses, " ", str, NULL)) + return log_oom(); + } + + r = sd_bus_message_exit_container(m); + if (r < 0) + return bus_log_parse_error(r); + + if (all || !isempty(addresses)) + bus_print_property_value(name, expected_value, value, strempty(addresses)); + + return 1; + + } else if (STR_IN_SET(name, "BindPaths", "BindReadOnlyPaths")) { + _cleanup_free_ char *paths = NULL; + const char *source, *dest; + int ignore_enoent; + uint64_t rbind; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ssbt)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(ssbt)", &source, &dest, &ignore_enoent, &rbind)) > 0) { + _cleanup_free_ char *str = NULL; + + if (isempty(source)) + continue; + + if (asprintf(&str, "%s%s%s%s%s", + ignore_enoent ? "-" : "", + source, + isempty(dest) ? "" : ":", + strempty(dest), + rbind == MS_REC ? ":rbind" : "") < 0) + return log_oom(); + + if (!strextend_with_separator(&paths, " ", str, NULL)) + return log_oom(); + } + 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 (all || !isempty(paths)) + bus_print_property_value(name, expected_value, value, strempty(paths)); + + return 1; + + } else if (streq(name, "TemporaryFileSystem")) { + _cleanup_free_ char *paths = NULL; + const char *target, *option; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "(ss)"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read(m, "(ss)", &target, &option)) > 0) { + _cleanup_free_ char *str = NULL; + + if (isempty(target)) + continue; + + if (asprintf(&str, "%s%s%s", target, isempty(option) ? "" : ":", strempty(option)) < 0) + return log_oom(); + + if (!strextend_with_separator(&paths, " ", str, NULL)) + return log_oom(); + } + 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 (all || !isempty(paths)) + bus_print_property_value(name, expected_value, value, strempty(paths)); + + return 1; + + } else if (streq(name, "LogExtraFields")) { + _cleanup_free_ char *fields = NULL; + const void *p; + size_t sz; + + r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "ay"); + if (r < 0) + return bus_log_parse_error(r); + + while ((r = sd_bus_message_read_array(m, 'y', &p, &sz)) > 0) { + _cleanup_free_ char *str = NULL; + const char *eq; + + if (memchr(p, 0, sz)) + continue; + + eq = memchr(p, '=', sz); + if (!eq) + continue; + + if (!journal_field_valid(p, eq - (const char*) p, false)) + continue; + + str = malloc(sz + 1); + if (!str) + return log_oom(); + + memcpy(str, p, sz); + str[sz] = '\0'; + + if (!utf8_is_valid(str)) + continue; + + if (!strextend_with_separator(&fields, " ", str, NULL)) + return log_oom(); + } + 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 (all || !isempty(fields)) + bus_print_property_value(name, expected_value, value, strempty(fields)); return 1; } @@ -5424,10 +5825,8 @@ static int cat(int argc, char *argv[], void *userdata) { bool first = true; int r; - if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot remotely cat units."); - return -EINVAL; - } + if (arg_transport != BUS_TRANSPORT_LOCAL) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot remotely cat units."); r = lookup_paths_init(&lp, arg_scope, 0, arg_root); if (r < 0) @@ -5523,10 +5922,8 @@ static int set_property(int argc, char *argv[], void *userdata) { return log_error_errno(r, "Failed to mangle unit name: %m"); t = unit_name_to_type(n); - if (t < 0) { - log_error("Invalid unit type: %s", n); - return -EINVAL; - } + if (t < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid unit type: %s", n); r = sd_bus_message_append(m, "sb", n, arg_runtime); if (r < 0) @@ -5764,15 +6161,11 @@ static int switch_root(int argc, char *argv[], void *userdata) { sd_bus *bus; int r; - if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot switch root remotely."); - return -EINVAL; - } + if (arg_transport != BUS_TRANSPORT_LOCAL) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot switch root remotely."); - if (argc < 2 || argc > 3) { - log_error("Wrong number of arguments."); - return -EINVAL; - } + if (argc < 2 || argc > 3) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Wrong number of arguments."); root = argv[1]; @@ -5799,10 +6192,11 @@ static int switch_root(int argc, char *argv[], void *userdata) { init = NULL; } - /* Instruct PID1 to exclude us from its killing spree applied during - * the transition. Otherwise we would exit with a failure status even - * though the switch to the new root has succeed. */ - argv_cmdline[0] = '@'; + /* Instruct PID1 to exclude us from its killing spree applied during the transition. Otherwise we + * would exit with a failure status even though the switch to the new root has succeed. */ + assert(saved_argv); + assert(saved_argv[0]); + saved_argv[0][0] = '@'; r = acquire_bus(BUS_MANAGER, &bus); if (r < 0) @@ -5910,10 +6304,8 @@ static int import_environment(int argc, char *argv[], void *userdata) { STRV_FOREACH(a, strv_skip(argv, 1)) { - if (!env_name_is_valid(*a)) { - log_error("Not a valid environment variable name: %s", *a); - return -EINVAL; - } + if (!env_name_is_valid(*a)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Not a valid environment variable name: %s", *a); STRV_FOREACH(b, environ) { const char *eq; @@ -5972,13 +6364,13 @@ static int enable_sysv_units(const char *verb, char **args) { const char *argv[] = { ROOTLIBEXECDIR "/systemd-sysv-install", - NULL, - NULL, - NULL, + NULL, /* --root= */ + NULL, /* verb */ + NULL, /* service */ NULL, }; - _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL; + _cleanup_free_ char *p = NULL, *q = NULL, *l = NULL, *v = NULL; bool found_native = false, found_sysv; const char *name; unsigned c = 1; @@ -6019,10 +6411,21 @@ static int enable_sysv_units(const char *verb, char **args) { log_info("%s is not a native service, redirecting to systemd-sysv-install.", name); } - if (!isempty(arg_root)) - argv[c++] = q = strappend("--root=", arg_root); + if (!isempty(arg_root)) { + q = strappend("--root=", arg_root); + if (!q) + return log_oom(); - argv[c++] = verb; + argv[c++] = q; + } + + /* Let's copy the verb, since it's still pointing directly into the original argv[] array we + * got passed, but safe_fork() is likely going to rewrite that for the new child */ + v = strdup(verb); + if (!v) + return log_oom(); + + argv[c++] = v; argv[c++] = basename(p); argv[c] = NULL; @@ -6066,7 +6469,7 @@ static int enable_sysv_units(const char *verb, char **args) { assert(f > 0); f--; assert(args[f] == name); - strv_remove(args, name); + strv_remove(args + f, name); } #endif @@ -6807,7 +7210,7 @@ static int create_edit_temp_file(const char *new_path, const char *original_path if (r < 0) return log_error_errno(r, "Failed to create directories for \"%s\": %m", new_path); - r = copy_file(original_path, t, 0, 0644, 0, COPY_REFLINK); + r = copy_file(original_path, t, 0, 0644, 0, 0, COPY_REFLINK); if (r == -ENOENT) { r = touch(t); @@ -7084,15 +7487,11 @@ static int edit(int argc, char *argv[], void *userdata) { sd_bus *bus; int r; - if (!on_tty()) { - log_error("Cannot edit units if not on a tty."); - return -EINVAL; - } + if (!on_tty()) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units if not on a tty."); - if (arg_transport != BUS_TRANSPORT_LOCAL) { - log_error("Cannot edit units remotely."); - return -EINVAL; - } + if (arg_transport != BUS_TRANSPORT_LOCAL) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit units remotely."); r = lookup_paths_init(&lp, arg_scope, 0, arg_root); if (r < 0) @@ -7110,10 +7509,8 @@ static int edit(int argc, char *argv[], void *userdata) { 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; - } + if (r > 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit %s: unit is masked.", *tmp); } r = find_paths_to_edit(bus, names, &paths); @@ -7180,7 +7577,7 @@ static int systemctl_help(void) { if (r < 0) return log_oom(); - printf("%s [OPTIONS...] {COMMAND} ...\n\n" + printf("%1$s [OPTIONS...] {COMMAND} ...\n\n" "Query or send control commands to the systemd manager.\n\n" " -h --help Show this help\n" " --version Show package version\n" @@ -7192,16 +7589,18 @@ static int systemctl_help(void) { " Operate on local container\n" " -t --type=TYPE List units of a particular type\n" " --state=STATE List units with particular LOAD or SUB or ACTIVE state\n" + " --failed Shorcut for --state=failed\n" " -p --property=NAME Show only properties by this name\n" " -a --all Show all properties/all units currently in memory,\n" " including dead/empty ones. To list all units installed on\n" " the system, use the 'list-unit-files' command instead.\n" - " --failed Same as --state=failed\n" " -l --full Don't ellipsize unit names on output\n" " -r --recursive Show unit list of host and local containers\n" " --reverse Show reverse dependencies with 'list-dependencies'\n" " --job-mode=MODE Specify how to deal with already queued jobs, when\n" " queueing a new job\n" + " -T --show-transaction\n" + " When enqueuing a unit job, show full transaction\n" " --show-types When showing sockets, explicitly show their type\n" " --value When showing properties, only print the value\n" " -i --ignore-inhibitors\n" @@ -7234,8 +7633,12 @@ static int systemctl_help(void) { " short-monotonic, short-unix,\n" " verbose, export, json, json-pretty, json-sse, cat)\n" " --firmware-setup Tell the firmware to show the setup menu on next boot\n" + " --boot-loader-menu=TIME\n" + " Boot into boot loader menu on next boot\n" + " --boot-loader-entry=NAME\n" + " Boot into a specific boot loader entry on next boot\n" " --plain Print unit dependencies as a list instead of a tree\n\n" - "Unit Commands:\n" + "%3$sUnit Commands:%4$s\n" " list-units [PATTERN...] List units currently in memory\n" " list-sockets [PATTERN...] List socket units currently in memory,\n" " ordered by address\n" @@ -7265,7 +7668,7 @@ static int systemctl_help(void) { " list-dependencies [UNIT] Recursively show units which are required\n" " or wanted by this unit or by which this\n" " unit is required or wanted\n\n" - "Unit File Commands:\n" + "%3$sUnit File Commands:%4$s\n" " list-unit-files [PATTERN...] List installed unit files\n" " enable [UNIT...|PATH...] Enable one or more unit files\n" " disable UNIT... Disable one or more unit files\n" @@ -7288,20 +7691,20 @@ static int systemctl_help(void) { " edit UNIT... Edit one or more unit files\n" " get-default Get the name of the default target\n" " set-default TARGET Set the default target\n\n" - "Machine Commands:\n" + "%3$sMachine Commands:%4$s\n" " list-machines [PATTERN...] List local containers and host\n\n" - "Job Commands:\n" + "%3$sJob Commands:%4$s\n" " list-jobs [PATTERN...] List jobs\n" " cancel [JOB...] Cancel all, one, or more jobs\n\n" - "Environment Commands:\n" + "%3$sEnvironment Commands:%4$s\n" " show-environment Dump environment\n" " set-environment VARIABLE=VALUE... Set one or more environment variables\n" " unset-environment VARIABLE... Unset one or more environment variables\n" " import-environment [VARIABLE...] Import all or some environment variables\n\n" - "Manager Lifecycle Commands:\n" + "%3$sManager Lifecycle Commands:%4$s\n" " daemon-reload Reload systemd manager configuration\n" " daemon-reexec Reexecute systemd manager\n\n" - "System Commands:\n" + "%3$sSystem Commands:%4$s\n" " is-system-running Check whether system is fully running\n" " default Enter system default mode\n" " rescue Enter system rescue mode\n" @@ -7317,9 +7720,10 @@ static int systemctl_help(void) { " 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" - "\nSee the %s for details.\n" + "\nSee the %2$s for details.\n" , program_invocation_short_name , link + , ansi_underline(), ansi_normal() ); return 0; @@ -7488,6 +7892,37 @@ static void help_states(void) { DUMP_STRING_TABLE(timer_state, TimerState, _TIMER_STATE_MAX); } +static int help_boot_loader_entry(void) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_free_ char **l = NULL; + sd_bus *bus; + char **i; + int r; + + r = acquire_bus(BUS_FULL, &bus); + if (r < 0) + return r; + + r = sd_bus_get_property_strv( + bus, + "org.freedesktop.login1", + "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", + "BootLoaderEntries", + &error, + &l); + if (r < 0) + return log_error_errno(r, "Failed to enumerate boot loader entries: %s", bus_error_message(&error, r)); + + if (strv_isempty(l)) + return log_error_errno(SYNTHETIC_ERRNO(ENODATA), "No boot loader entries discovered."); + + STRV_FOREACH(i, l) + puts(*i); + + return 0; +} + static int systemctl_parse_argv(int argc, char *argv[]) { enum { ARG_FAIL = 0x100, @@ -7518,6 +7953,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { ARG_JOB_MODE, ARG_PRESET_MODE, ARG_FIRMWARE_SETUP, + ARG_BOOT_LOADER_MENU, + ARG_BOOT_LOADER_ENTRY, ARG_NOW, ARG_MESSAGE, ARG_WAIT, @@ -7567,12 +8004,14 @@ static int systemctl_parse_argv(int argc, char *argv[]) { { "recursive", no_argument, NULL, 'r' }, { "preset-mode", required_argument, NULL, ARG_PRESET_MODE }, { "firmware-setup", no_argument, NULL, ARG_FIRMWARE_SETUP }, + { "boot-loader-menu", required_argument, NULL, ARG_BOOT_LOADER_MENU }, + { "boot-loader-entry", required_argument, NULL, ARG_BOOT_LOADER_ENTRY }, { "now", no_argument, NULL, ARG_NOW }, { "message", required_argument, NULL, ARG_MESSAGE }, + { "show-transaction", no_argument, NULL, 'T' }, {} }; - const char *p; int c, r; assert(argc >= 0); @@ -7581,7 +8020,7 @@ static int systemctl_parse_argv(int argc, char *argv[]) { /* we default to allowing interactive authorization only in systemctl (not in the legacy commands) */ arg_ask_password = true; - while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:ir.::", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "ht:p:alqfs:H:M:n:o:iTr.::", options, NULL)) >= 0) switch (c) { @@ -7592,6 +8031,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return version(); case 't': { + const char *p; + if (isempty(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--type= requires arguments."); @@ -7611,9 +8052,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { } if (unit_type_from_string(type) >= 0) { - if (strv_push(&arg_types, type) < 0) + if (strv_consume(&arg_types, TAKE_PTR(type)) < 0) return log_oom(); - type = NULL; continue; } @@ -7622,9 +8062,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { * in --types= too for compatibility * with old versions */ if (unit_load_state_from_string(type) >= 0) { - if (strv_push(&arg_states, type) < 0) + if (strv_consume(&arg_states, TAKE_PTR(type)) < 0) return log_oom(); - type = NULL; continue; } @@ -7636,14 +8075,16 @@ static int systemctl_parse_argv(int argc, char *argv[]) { break; } - case 'p': { - /* Make sure that if the empty property list - was specified, we won't show any properties. */ + case 'p': + /* Make sure that if the empty property list was specified, we won't show any + properties. */ if (isempty(optarg) && !arg_properties) { arg_properties = new0(char*, 1); if (!arg_properties) return log_oom(); - } else + } else { + const char *p; + for (p = optarg;;) { _cleanup_free_ char *prop = NULL; @@ -7653,11 +8094,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { if (r == 0) break; - if (strv_push(&arg_properties, prop) < 0) + if (strv_consume(&arg_properties, TAKE_PTR(prop)) < 0) return log_oom(); - - prop = NULL; } + } /* If the user asked for a particular * property, show it to him, even if it is @@ -7665,7 +8105,6 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_all = true; break; - } case 'a': arg_all = true; @@ -7840,7 +8279,30 @@ static int systemctl_parse_argv(int argc, char *argv[]) { arg_firmware_setup = true; break; + case ARG_BOOT_LOADER_MENU: + + r = parse_sec(optarg, &arg_boot_loader_menu); + if (r < 0) + return log_error_errno(r, "Failed to parse --boot-loader-menu= argument '%s': %m", optarg); + + break; + + case ARG_BOOT_LOADER_ENTRY: + + if (streq(optarg, "help")) { /* Yes, this means, "help" is not a valid boot loader entry name we can deal with */ + r = help_boot_loader_entry(); + if (r < 0) + return r; + + return 0; + } + + arg_boot_loader_entry = empty_to_null(optarg); + break; + case ARG_STATE: { + const char *p; + if (isempty(optarg)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "--state= requires arguments."); @@ -7859,10 +8321,8 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return 0; } - if (strv_push(&arg_states, s) < 0) + if (strv_consume(&arg_states, TAKE_PTR(s)) < 0) return log_oom(); - - s = NULL; } break; } @@ -7897,6 +8357,10 @@ static int systemctl_parse_argv(int argc, char *argv[]) { return log_oom(); break; + case 'T': + arg_show_transaction = true; + break; + case '.': /* Output an error mimicking getopt, and print a hint afterwards */ log_error("%s: invalid option -- '.'", program_invocation_name); @@ -8005,7 +8469,7 @@ static int halt_parse_argv(int argc, char *argv[]) { } if (arg_action == ACTION_REBOOT && (argc == optind || argc == optind + 1)) { - r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL); + r = update_reboot_parameter_and_warn(argc == optind + 1 ? argv[optind] : NULL, false); if (r < 0) return r; } else if (optind < argc) @@ -8015,56 +8479,6 @@ static int halt_parse_argv(int argc, char *argv[]) { return 1; } -static int parse_shutdown_time_spec(const char *t, usec_t *_u) { - assert(t); - assert(_u); - - if (streq(t, "now")) - *_u = 0; - else if (!strchr(t, ':')) { - uint64_t u; - - if (safe_atou64(t, &u) < 0) - return -EINVAL; - - *_u = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; - } else { - char *e = NULL; - long hour, minute; - struct tm tm = {}; - time_t s; - usec_t n; - - errno = 0; - hour = strtol(t, &e, 10); - if (errno > 0 || *e != ':' || hour < 0 || hour > 23) - return -EINVAL; - - minute = strtol(e+1, &e, 10); - if (errno > 0 || *e != 0 || minute < 0 || minute > 59) - return -EINVAL; - - n = now(CLOCK_REALTIME); - s = (time_t) (n / USEC_PER_SEC); - - assert_se(localtime_r(&s, &tm)); - - tm.tm_hour = (int) hour; - tm.tm_min = (int) minute; - tm.tm_sec = 0; - - s = mktime(&tm); - assert(s >= 0); - - *_u = (usec_t) s * USEC_PER_SEC; - - while (*_u <= n) - *_u += USEC_PER_DAY; - } - - return 0; -} - static int shutdown_parse_argv(int argc, char *argv[]) { enum { ARG_HELP = 0x100, @@ -8365,52 +8779,10 @@ _pure_ static int action_to_runlevel(void) { }; assert(arg_action >= 0 && arg_action < _ACTION_MAX); - return table[arg_action]; } #endif -static int talk_initctl(void) { -#if HAVE_SYSV_COMPAT - struct init_request request = { - .magic = INIT_MAGIC, - .sleeptime = 0, - .cmd = INIT_CMD_RUNLVL - }; - - _cleanup_close_ int fd = -1; - char rl; - int r; - const char *p; - - rl = action_to_runlevel(); - if (!rl) - return 0; - - request.runlevel = rl; - - FOREACH_STRING(p, "/run/initctl", "/dev/initctl") { - fd = open(p, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); - if (fd >= 0 || errno != ENOENT) - break; - } - if (fd < 0) { - if (errno == ENOENT) - return 0; - - return log_error_errno(errno, "Failed to open initctl fifo: %m"); - } - - r = loop_write(fd, &request, sizeof(request), false); - if (r < 0) - return log_error_errno(r, "Failed to write to %s: %m", p); - - return 1; -#else - return 0; -#endif -} - static int systemctl_main(int argc, char *argv[]) { static const Verb verbs[] = { { "list-units", VERB_ANY, VERB_ANY, VERB_DEFAULT|VERB_ONLINE_ONLY, list_units }, @@ -8502,12 +8874,14 @@ static int reload_with_fallback(void) { static int start_with_fallback(void) { /* First, try systemd via D-Bus. */ - if (start_unit(0, NULL, NULL) >= 0) + if (start_unit(0, NULL, NULL) == 0) return 0; +#if HAVE_SYSV_COMPAT /* Nothing else worked, so let's try /dev/initctl */ - if (talk_initctl() > 0) + if (talk_initctl(action_to_runlevel()) > 0) return 0; +#endif return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to talk to init daemon."); @@ -8607,8 +8981,8 @@ static int logind_schedule_shutdown(void) { log_info("Shutdown scheduled for %s, use 'shutdown -c' to cancel.", format_timestamp(date, sizeof(date), arg_when)); return 0; #else - log_error("Cannot schedule shutdown without logind support, proceeding with immediate shutdown."); - return -ENOSYS; + return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), + "Cannot schedule shutdown without logind support, proceeding with immediate shutdown."); #endif } @@ -8714,16 +9088,14 @@ static int logind_cancel_shutdown(void) { return 0; #else - log_error("Not compiled with logind support, cannot cancel scheduled shutdowns."); - return -ENOSYS; + return log_error_errno(SYNTHETIC_ERRNO(ENOSYS), + "Not compiled with logind support, cannot cancel scheduled shutdowns."); #endif } static int run(int argc, char *argv[]) { int r; - argv_cmdline = argv[0]; - setlocale(LC_ALL, ""); log_parse_environment(); log_open(); @@ -8808,14 +9180,6 @@ static int run(int argc, char *argv[]) { finish: release_busses(); - strv_free(arg_types); - strv_free(arg_states); - strv_free(arg_properties); - - strv_free(arg_wall); - free(arg_root); - free(arg_esp_path); - /* Note that we return r here, not 0, so that we can implement the LSB-like return codes */ return r; } diff --git a/src/systemctl/sysv-compat.c b/src/systemctl/sysv-compat.c new file mode 100644 index 000000000..0283daad8 --- /dev/null +++ b/src/systemctl/sysv-compat.c @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include +#include +#include + +#include "fd-util.h" +#include "initreq.h" +#include "io-util.h" +#include "parse-util.h" +#include "strv.h" +#include "sysv-compat.h" + +#if HAVE_SYSV_COMPAT +int talk_initctl(char rl) { + struct init_request request; + _cleanup_close_ int fd = -1; + const char *p; + int r; + + /* Try to switch to the specified SysV runlevel. Returns == 0 if the operation does not apply on this + * system, and > 0 on success. */ + + if (rl == 0) + return 0; + + FOREACH_STRING(p, "/run/initctl", "/dev/initctl") { + fd = open(p, O_WRONLY|O_NONBLOCK|O_CLOEXEC|O_NOCTTY); + if (fd >= 0 || errno != ENOENT) + break; + } + if (fd < 0) { + if (errno == ENOENT) + return 0; + + return log_error_errno(errno, "Failed to open initctl fifo: %m"); + } + + request = (struct init_request) { + .magic = INIT_MAGIC, + .sleeptime = 0, + .cmd = INIT_CMD_RUNLVL, + .runlevel = rl, + }; + + r = loop_write(fd, &request, sizeof(request), false); + if (r < 0) + return log_error_errno(r, "Failed to write to %s: %m", p); + + return 1; +} +#endif + +int parse_shutdown_time_spec(const char *t, usec_t *ret) { + assert(t); + assert(ret); + + if (streq(t, "now")) + *ret = 0; + else if (!strchr(t, ':')) { + uint64_t u; + + if (safe_atou64(t, &u) < 0) + return -EINVAL; + + *ret = now(CLOCK_REALTIME) + USEC_PER_MINUTE * u; + } else { + char *e = NULL; + long hour, minute; + struct tm tm = {}; + time_t s; + usec_t n; + + errno = 0; + hour = strtol(t, &e, 10); + if (errno > 0 || *e != ':' || hour < 0 || hour > 23) + return -EINVAL; + + minute = strtol(e+1, &e, 10); + if (errno > 0 || *e != 0 || minute < 0 || minute > 59) + return -EINVAL; + + n = now(CLOCK_REALTIME); + s = (time_t) (n / USEC_PER_SEC); + + assert_se(localtime_r(&s, &tm)); + + tm.tm_hour = (int) hour; + tm.tm_min = (int) minute; + tm.tm_sec = 0; + + s = mktime(&tm); + assert(s >= 0); + + *ret = (usec_t) s * USEC_PER_SEC; + + while (*ret <= n) + *ret += USEC_PER_DAY; + } + + return 0; +} diff --git a/src/systemctl/sysv-compat.h b/src/systemctl/sysv-compat.h new file mode 100644 index 000000000..e79919217 --- /dev/null +++ b/src/systemctl/sysv-compat.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ +#pragma once + +#include "time-util.h" + +#if HAVE_SYSV_COMPAT +int talk_initctl(char runlevel); +#endif + +int parse_shutdown_time_spec(const char *t, usec_t *ret); + +/* The init script exit codes for the LSB 'status' verb. (This is different from the 'start' verb, whose exit + codes are defined in exit-status.h.) + + 0 program is running or service is OK + 1 program is dead and /var/run pid file exists + 2 program is dead and /var/lock lock file exists + 3 program is not running + 4 program or service status is unknown + 5-99 reserved for future LSB use + 100-149 reserved for distribution use + 150-199 reserved for application use + 200-254 reserved + + https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html +*/ +enum { + EXIT_PROGRAM_RUNNING_OR_SERVICE_OK = 0, + EXIT_PROGRAM_DEAD_AND_PID_EXISTS = 1, + EXIT_PROGRAM_DEAD_AND_LOCK_FILE_EXISTS = 2, + EXIT_PROGRAM_NOT_RUNNING = 3, + EXIT_PROGRAM_OR_SERVICES_STATUS_UNKNOWN = 4, +}; diff --git a/src/systemd/sd-bus-vtable.h b/src/systemd/sd-bus-vtable.h index 4350a8c70..8a73ef050 100644 --- a/src/systemd/sd-bus-vtable.h +++ b/src/systemd/sd-bus-vtable.h @@ -48,6 +48,10 @@ enum { #define SD_BUS_VTABLE_CAPABILITY(x) ((uint64_t) (((x)+1) & 0xFFFF) << 40) +enum { + _SD_BUS_VTABLE_PARAM_NAMES = 1 << 0, +}; + struct sd_bus_vtable { /* Please do not initialize this structure directly, use the * macros below instead */ @@ -57,6 +61,7 @@ struct sd_bus_vtable { union { struct { size_t element_size; + uint64_t features; } start; struct { const char *member; @@ -64,10 +69,12 @@ struct sd_bus_vtable { const char *result; sd_bus_message_handler_t handler; size_t offset; + const char *names; } method; struct { const char *member; const char *signature; + const char *names; } signal; struct { const char *member; @@ -85,12 +92,16 @@ struct sd_bus_vtable { .flags = _flags, \ .x = { \ .start = { \ - .element_size = sizeof(sd_bus_vtable) \ + .element_size = sizeof(sd_bus_vtable), \ + .features = _SD_BUS_VTABLE_PARAM_NAMES \ }, \ }, \ } -#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ +/* helper macro to format method and signal parameters, one at a time */ +#define SD_BUS_PARAM(x) #x "\0" + +#define SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, _in_names, _result, _out_names, _handler, _offset, _flags) \ { \ .type = _SD_BUS_VTABLE_METHOD, \ .flags = _flags, \ @@ -101,13 +112,18 @@ struct sd_bus_vtable { .result = _result, \ .handler = _handler, \ .offset = _offset, \ + .names = _in_names _out_names, \ }, \ }, \ } +#define SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, _offset, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, "", _result, "", _handler, _offset, _flags) +#define SD_BUS_METHOD_WITH_NAMES(_member, _signature, _in_names, _result, _out_names, _handler, _flags) \ + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, _in_names, _result, _out_names, _handler, 0, _flags) #define SD_BUS_METHOD(_member, _signature, _result, _handler, _flags) \ - SD_BUS_METHOD_WITH_OFFSET(_member, _signature, _result, _handler, 0, _flags) + SD_BUS_METHOD_WITH_NAMES_OFFSET(_member, _signature, "", _result, "", _handler, 0, _flags) -#define SD_BUS_SIGNAL(_member, _signature, _flags) \ +#define SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, _out_names, _flags) \ { \ .type = _SD_BUS_VTABLE_SIGNAL, \ .flags = _flags, \ @@ -115,9 +131,12 @@ struct sd_bus_vtable { .signal = { \ .member = _member, \ .signature = _signature, \ + .names = _out_names, \ }, \ }, \ } +#define SD_BUS_SIGNAL(_member, _signature, _flags) \ + SD_BUS_SIGNAL_WITH_NAMES(_member, _signature, "", _flags) #define SD_BUS_PROPERTY(_member, _signature, _get, _offset, _flags) \ { \ diff --git a/src/systemd/sd-bus.h b/src/systemd/sd-bus.h index 129cc9332..84ceb62dc 100644 --- a/src/systemd/sd-bus.h +++ b/src/systemd/sd-bus.h @@ -33,6 +33,12 @@ _SD_BEGIN_DECLARATIONS; #define SD_BUS_DEFAULT_USER ((sd_bus *) 2) #define SD_BUS_DEFAULT_SYSTEM ((sd_bus *) 3) +/* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-marshaling-signature */ +#define SD_BUS_MAXIMUM_SIGNATURE_LENGTH 255 + +/* https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names */ +#define SD_BUS_MAXIMUM_NAME_LENGTH 255 + /* Types */ typedef struct sd_bus sd_bus; diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index 4875f1055..d299c7912 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -39,7 +39,7 @@ int sd_dhcp_lease_get_t1(sd_dhcp_lease *lease, uint32_t *t1); int sd_dhcp_lease_get_t2(sd_dhcp_lease *lease, uint32_t *t2); int sd_dhcp_lease_get_broadcast(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_netmask(sd_dhcp_lease *lease, struct in_addr *addr); -int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, struct in_addr *addr); +int sd_dhcp_lease_get_router(sd_dhcp_lease *lease, const struct in_addr **addr); int sd_dhcp_lease_get_next_server(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_server_identifier(sd_dhcp_lease *lease, struct in_addr *addr); int sd_dhcp_lease_get_dns(sd_dhcp_lease *lease, const struct in_addr **addr); diff --git a/src/systemd/sd-event.h b/src/systemd/sd-event.h index 787a12f24..7bb860937 100644 --- a/src/systemd/sd-event.h +++ b/src/systemd/sd-event.h @@ -71,7 +71,7 @@ typedef int (*sd_event_handler_t)(sd_event_source *s, void *userdata); typedef int (*sd_event_io_handler_t)(sd_event_source *s, int fd, uint32_t revents, void *userdata); typedef int (*sd_event_time_handler_t)(sd_event_source *s, uint64_t usec, void *userdata); typedef int (*sd_event_signal_handler_t)(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata); -#if defined _GNU_SOURCE || _POSIX_C_SOURCE >= 199309L +#if defined _GNU_SOURCE || (defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 199309L) typedef int (*sd_event_child_handler_t)(sd_event_source *s, const siginfo_t *si, void *userdata); #else typedef void* sd_event_child_handler_t; diff --git a/src/systemd/sd-netlink.h b/src/systemd/sd-netlink.h index 9e6e437ba..804fe9f03 100644 --- a/src/systemd/sd-netlink.h +++ b/src/systemd/sd-netlink.h @@ -34,7 +34,13 @@ typedef struct sd_netlink sd_netlink; typedef struct sd_genl_socket sd_genl_socket; typedef struct sd_netlink_message sd_netlink_message; typedef struct sd_netlink_slot sd_netlink_slot; -typedef enum {SD_GENL_ID_CTRL, SD_GENL_WIREGUARD, SD_GENL_FOU} sd_genl_family; + +typedef enum sd_gen_family { + SD_GENL_ID_CTRL, + SD_GENL_WIREGUARD, + SD_GENL_FOU, + SD_GENL_L2TP, +} sd_genl_family; /* callback */ diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index cc6bca9f5..166c30185 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -89,6 +89,7 @@ int sd_network_link_get_setup_state(int ifindex, char **state); * -ENODATA: networkd is not aware of the link */ int sd_network_link_get_operational_state(int ifindex, char **state); +int sd_network_link_get_required_operstate_for_online(int ifindex, char **state); /* Indicates whether the network is relevant to being online. * Possible return codes: diff --git a/src/sysusers/sysusers.c b/src/sysusers/sysusers.c index df28bcfd7..843c3837d 100644 --- a/src/sysusers/sysusers.c +++ b/src/sysusers/sysusers.c @@ -244,7 +244,7 @@ static int make_backup(const char *target, const char *x) { return 0; fail: - unlink(temp); + (void) unlink(temp); return r; } @@ -389,15 +389,15 @@ static int write_temporary_passwd(const char *passwd_path, FILE **tmpfile, char while ((r = fgetpwent_sane(original, &pw)) > 0) { i = ordered_hashmap_get(users, pw->pw_name); - if (i && i->todo_user) { - log_error("%s: User \"%s\" already exists.", passwd_path, pw->pw_name); - return -EEXIST; - } + if (i && i->todo_user) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: User \"%s\" already exists.", + passwd_path, pw->pw_name); - if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) { - log_error("%s: Detected collision for UID " UID_FMT ".", passwd_path, pw->pw_uid); - return -EEXIST; - } + if (ordered_hashmap_contains(todo_uids, UID_TO_PTR(pw->pw_uid))) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Detected collision for UID " UID_FMT ".", + passwd_path, pw->pw_uid); /* Make sure we keep the NIS entries (if any) at the end. */ if (IN_SET(pw->pw_name[0], '+', '-')) @@ -592,15 +592,15 @@ static int write_temporary_group(const char *group_path, FILE **tmpfile, char ** * step that we don't generate duplicate entries. */ i = ordered_hashmap_get(groups, gr->gr_name); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", group_path, gr->gr_name); - return -EEXIST; - } + if (i && i->todo_group) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Group \"%s\" already exists.", + group_path, gr->gr_name); - if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) { - log_error("%s: Detected collision for GID " GID_FMT ".", group_path, gr->gr_gid); - return -EEXIST; - } + if (ordered_hashmap_contains(todo_gids, GID_TO_PTR(gr->gr_gid))) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Detected collision for GID " GID_FMT ".", + group_path, gr->gr_gid); /* Make sure we keep the NIS entries (if any) at the end. */ if (IN_SET(gr->gr_name[0], '+', '-')) @@ -687,10 +687,10 @@ static int write_temporary_gshadow(const char * gshadow_path, FILE **tmpfile, ch while ((r = fgetsgent_sane(original, &sg)) > 0) { i = ordered_hashmap_get(groups, sg->sg_namp); - if (i && i->todo_group) { - log_error("%s: Group \"%s\" already exists.", gshadow_path, sg->sg_namp); - return -EEXIST; - } + if (i && i->todo_group) + return log_error_errno(SYNTHETIC_ERRNO(EEXIST), + "%s: Group \"%s\" already exists.", + gshadow_path, sg->sg_namp); r = putsgent_with_members(sg, gshadow); if (r < 0) @@ -1021,10 +1021,8 @@ static int add_user(Item *i) { if (!i->uid_set) { for (;;) { r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free user ID available for %s.", i->name); - return r; - } + if (r < 0) + return log_error_errno(r, "No free user ID available for %s.", i->name); r = uid_is_ok(search_uid, i->name, true); if (r < 0) @@ -1178,10 +1176,8 @@ static int add_group(Item *i) { for (;;) { /* We look for new GIDs in the UID pool! */ r = uid_range_next_lower(uid_range, n_uid_range, &search_uid); - if (r < 0) { - log_error("No free group ID available for %s.", i->name); - return r; - } + if (r < 0) + return log_error_errno(r, "No free group ID available for %s.", i->name); r = gid_is_ok(search_uid); if (r < 0) @@ -1381,7 +1377,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { *id = NULL, *resolved_id = NULL, *description = NULL, *resolved_description = NULL, *home = NULL, *resolved_home = NULL, - *shell, *resolved_shell = NULL; + *shell = NULL, *resolved_shell = NULL; _cleanup_(item_freep) Item *i = NULL; Item *existing; OrderedHashmap *h; @@ -1396,135 +1392,121 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { p = buffer; r = extract_many_words(&p, NULL, EXTRACT_QUOTES, &action, &name, &id, &description, &home, &shell, NULL); - if (r < 0) { - log_error("[%s:%u] Syntax error.", fname, line); - return r; - } - if (r < 2) { - log_error("[%s:%u] Missing action and name columns.", fname, line); - return -EINVAL; - } - if (!isempty(p)) { - log_error("[%s:%u] Trailing garbage.", fname, line); - return -EINVAL; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Syntax error.", fname, line); + if (r < 2) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Missing action and name columns.", fname, line); + if (!isempty(p)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Trailing garbage.", fname, line); /* Verify action */ - if (strlen(action) != 1) { - log_error("[%s:%u] Unknown modifier '%s'", fname, line, action); - return -EINVAL; - } + if (strlen(action) != 1) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Unknown modifier '%s'", fname, line, action); - if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) { - log_error("[%s:%u] Unknown command type '%c'.", fname, line, action[0]); - return -EBADMSG; - } + if (!IN_SET(action[0], ADD_USER, ADD_GROUP, ADD_MEMBER, ADD_RANGE)) + return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), + "[%s:%u] Unknown command type '%c'.", fname, line, action[0]); /* Verify name */ - if (isempty(name) || streq(name, "-")) + if (empty_or_dash(name)) name = mfree(name); if (name) { r = specifier_printf(name, specifier_table, NULL, &resolved_name); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); - return r; - } + if (r < 0) + log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", fname, line, name); - if (!valid_user_group_name(resolved_name)) { - log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_name); - return -EINVAL; - } + if (!valid_user_group_name(resolved_name)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid user or group name.", + fname, line, resolved_name); } /* Verify id */ - if (isempty(id) || streq(id, "-")) + if (empty_or_dash(id)) id = mfree(id); if (id) { r = specifier_printf(id, specifier_table, NULL, &resolved_id); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, name); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, name); } /* Verify description */ - if (isempty(description) || streq(description, "-")) + if (empty_or_dash(description)) description = mfree(description); if (description) { r = specifier_printf(description, specifier_table, NULL, &resolved_description); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, description); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, description); - if (!valid_gecos(resolved_description)) { - log_error("[%s:%u] '%s' is not a valid GECOS field.", fname, line, resolved_description); - return -EINVAL; - } + if (!valid_gecos(resolved_description)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid GECOS field.", + fname, line, resolved_description); } /* Verify home */ - if (isempty(home) || streq(home, "-")) + if (empty_or_dash(home)) home = mfree(home); if (home) { r = specifier_printf(home, specifier_table, NULL, &resolved_home); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, home); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, home); - if (!valid_home(resolved_home)) { - log_error("[%s:%u] '%s' is not a valid home directory field.", fname, line, resolved_home); - return -EINVAL; - } + if (!valid_home(resolved_home)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid home directory field.", + fname, line, resolved_home); } /* Verify shell */ - if (isempty(shell) || streq(shell, "-")) + if (empty_or_dash(shell)) shell = mfree(shell); if (shell) { r = specifier_printf(shell, specifier_table, NULL, &resolved_shell); - if (r < 0) { - log_error("[%s:%u] Failed to replace specifiers: %s", fname, line, shell); - return r; - } + if (r < 0) + return log_error_errno(r, "[%s:%u] Failed to replace specifiers: %s", + fname, line, shell); - if (!valid_shell(resolved_shell)) { - log_error("[%s:%u] '%s' is not a valid login shell field.", fname, line, resolved_shell); - return -EINVAL; - } + if (!valid_shell(resolved_shell)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid login shell field.", + fname, line, resolved_shell); } switch (action[0]) { case ADD_RANGE: - if (resolved_name) { - log_error("[%s:%u] Lines of type 'r' don't take a name field.", fname, line); - return -EINVAL; - } + if (resolved_name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'r' don't take a name field.", + fname, line); - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'r' require a ID range in the third field.", fname, line); - return -EINVAL; - } + if (!resolved_id) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'r' require a ID range in the third field.", + fname, line); - if (description || home || shell) { - log_error("[%s:%u] Lines of type '%c' don't take a %s field.", - fname, line, action[0], - description ? "GECOS" : home ? "home directory" : "login shell"); - return -EINVAL; - } + if (description || home || shell) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type '%c' don't take a %s field.", + fname, line, action[0], + description ? "GECOS" : home ? "home directory" : "login shell"); r = uid_range_add_str(&uid_range, &n_uid_range, resolved_id); - if (r < 0) { - log_error("[%s:%u] Invalid UID range %s.", fname, line, resolved_id); - return -EINVAL; - } + if (r < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Invalid UID range %s.", fname, line, resolved_id); return 0; @@ -1532,27 +1514,26 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { char **l; /* Try to extend an existing member or group item */ - if (!name) { - log_error("[%s:%u] Lines of type 'm' require a user name in the second field.", fname, line); - return -EINVAL; - } + if (!name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'm' require a user name in the second field.", + fname, line); - if (!resolved_id) { - log_error("[%s:%u] Lines of type 'm' require a group name in the third field.", fname, line); - return -EINVAL; - } + if (!resolved_id) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'm' require a group name in the third field.", + fname, line); - if (!valid_user_group_name(resolved_id)) { - log_error("[%s:%u] '%s' is not a valid user or group name.", fname, line, resolved_id); - return -EINVAL; - } + if (!valid_user_group_name(resolved_id)) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] '%s' is not a valid user or group name.", + fname, line, resolved_id); - if (description || home || shell) { - log_error("[%s:%u] Lines of type '%c' don't take a %s field.", - fname, line, action[0], - description ? "GECOS" : home ? "home directory" : "login shell"); - return -EINVAL; - } + if (description || home || shell) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type '%c' don't take a %s field.", + fname, line, action[0], + description ? "GECOS" : home ? "home directory" : "login shell"); r = ordered_hashmap_ensure_allocated(&members, &members_hash_ops); if (r < 0) @@ -1591,10 +1572,10 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { } case ADD_USER: - if (!name) { - log_error("[%s:%u] Lines of type 'u' require a user name in the second field.", fname, line); - return -EINVAL; - } + if (!name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'u' require a user name in the second field.", + fname, line); r = ordered_hashmap_ensure_allocated(&users, &item_hash_ops); if (r < 0) @@ -1635,17 +1616,16 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { break; case ADD_GROUP: - if (!name) { - log_error("[%s:%u] Lines of type 'g' require a user name in the second field.", fname, line); - return -EINVAL; - } + if (!name) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type 'g' require a user name in the second field.", + fname, line); - if (description || home || shell) { - log_error("[%s:%u] Lines of type '%c' don't take a %s field.", - fname, line, action[0], - description ? "GECOS" : home ? "home directory" : "login shell"); - return -EINVAL; - } + if (description || home || shell) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "[%s:%u] Lines of type '%c' don't take a %s field.", + fname, line, action[0], + description ? "GECOS" : home ? "home directory" : "login shell"); r = ordered_hashmap_ensure_allocated(&groups, &item_hash_ops); if (r < 0) @@ -1680,7 +1660,6 @@ static int parse_line(const char *fname, unsigned line, const char *buffer) { existing = ordered_hashmap_get(h, i->name); if (existing) { - /* Two identical items are fine */ if (!item_equal(existing, i)) log_warning("Two or more conflicting lines for %s configured, ignoring.", i->name); diff --git a/src/test/meson.build b/src/test/meson.build index 08026ea8c..ae970cf8a 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -228,6 +228,11 @@ tests += [ [], []], + [['src/test/test-libmount.c'], + [], + [threads, + libmount]], + [['src/test/test-mount-util.c'], [], []], @@ -440,6 +445,10 @@ tests += [ [], []], + [['src/test/test-ordered-set.c'], + [], + []], + [['src/test/test-set-disable-mempool.c'], [], [threads]], @@ -560,6 +569,11 @@ tests += [ [], '', 'manual'], + [['src/test/test-cgroup-cpu.c'], + [libcore, + libshared], + []], + [['src/test/test-cgroup-mask.c', 'src/test/test-helper.c'], [libcore, @@ -705,11 +719,10 @@ tests += [ 'ENABLE_NSS', 'manual'], [['src/test/test-umount.c', - 'src/core/mount-setup.c', - 'src/core/mount-setup.h', - 'src/core/umount.c', - 'src/core/umount.h'], - [], + 'src/shutdown/umount.c', + 'src/shutdown/umount.h'], + [libcore_shared, + libshared], [libmount]], [['src/test/test-bus-util.c'], @@ -858,6 +871,10 @@ tests += [ [], [threads]], + [['src/libsystemd/sd-bus/test-bus-queue-ref-cycle.c'], + [], + [threads]], + [['src/libsystemd/sd-bus/test-bus-watch-bind.c'], [], [threads], '', 'timeout=120'], diff --git a/src/test/test-alloc-util.c b/src/test/test-alloc-util.c index 40c32d711..ad10eb178 100644 --- a/src/test/test-alloc-util.c +++ b/src/test/test-alloc-util.c @@ -4,7 +4,7 @@ #include "alloc-util.h" #include "macro.h" -#include "util.h" +#include "memory-util.h" static void test_alloca(void) { static const uint8_t zero[997] = { }; @@ -19,6 +19,25 @@ static void test_alloca(void) { assert_se(!memcmp(t, zero, 997)); } +static void test_GREEDY_REALLOC(void) { + _cleanup_free_ int *a = NULL, *b = NULL; + size_t n_allocated = 0, i; + + /* Give valgrind a chance to verify our realloc operations */ + + for (i = 0; i < 2048; i++) { + assert_se(GREEDY_REALLOC(a, n_allocated, i + 1)); + a[i] = i; + assert_se(GREEDY_REALLOC(a, n_allocated, i / 2)); + } + + for (i = 30, n_allocated = 0; i < 2048; i+=7) { + assert_se(GREEDY_REALLOC(b, n_allocated, i + 1)); + b[i] = i; + assert_se(GREEDY_REALLOC(b, n_allocated, i / 2)); + } +} + static void test_memdup_multiply_and_greedy_realloc(void) { int org[] = {1, 2, 3}; _cleanup_free_ int *dup; @@ -74,6 +93,7 @@ static void test_bool_assign(void) { int main(int argc, char *argv[]) { test_alloca(); + test_GREEDY_REALLOC(); test_memdup_multiply_and_greedy_realloc(); test_bool_assign(); diff --git a/src/test/test-async.c b/src/test/test-async.c index 4f5307889..7d8accc2f 100644 --- a/src/test/test-async.c +++ b/src/test/test-async.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include "async.h" diff --git a/src/test/test-barrier.c b/src/test/test-barrier.c index 6469129fa..10417f662 100644 --- a/src/test/test-barrier.c +++ b/src/test/test-barrier.c @@ -18,6 +18,7 @@ #include "util.h" #include "tests.h" #include "virt.h" +#include "time-util.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 diff --git a/src/test/test-boot-timestamps.c b/src/test/test-boot-timestamps.c index a79e0cf16..79b8dd49a 100644 --- a/src/test/test-boot-timestamps.c +++ b/src/test/test-boot-timestamps.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "acpi-fpdt.h" #include "boot-timestamps.h" #include "efivars.h" diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c index de5fa729c..563b99694 100644 --- a/src/test/test-cap-list.c +++ b/src/test/test-cap-list.c @@ -1,5 +1,6 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include "alloc-util.h" diff --git a/src/test/test-capability.c b/src/test/test-capability.c index 325d7c8cc..6b2de66bb 100644 --- a/src/test/test-capability.c +++ b/src/test/test-capability.c @@ -215,11 +215,32 @@ static void test_set_ambient_caps(void) { assert_se(prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) == 1); } +static void test_ensure_cap_64bit(void) { + _cleanup_free_ char *content = NULL; + unsigned long p = 0; + int r; + + r = read_one_line_file("/proc/sys/kernel/cap_last_cap", &content); + if (r == -ENOENT) /* kernel pre 3.2 */ + return; + assert_se(r >= 0); + + assert_se(safe_atolu(content, &p) >= 0); + + /* If caps don't fit into 64bit anymore, we have a problem, fail the test. */ + assert_se(p <= 63); + + /* Also check for the header definition */ + assert_se(CAP_LAST_CAP <= 63); +} + int main(int argc, char *argv[]) { bool run_ambient; test_setup_logging(LOG_INFO); + test_ensure_cap_64bit(); + test_last_cap_file(); test_last_cap_probe(); diff --git a/src/test/test-cgroup-cpu.c b/src/test/test-cgroup-cpu.c new file mode 100644 index 000000000..a445acc95 --- /dev/null +++ b/src/test/test-cgroup-cpu.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "cgroup.h" +#include "log.h" + +static void test_cgroup_cpu_adjust_period(void) { + log_info("/* %s */", __func__); + + /* Period 1ms, quota 40% -> Period 2.5ms */ + assert_se(2500 == cgroup_cpu_adjust_period(USEC_PER_MSEC, 400 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 10ms, quota 10% -> keep. */ + assert_se(10 * USEC_PER_MSEC == cgroup_cpu_adjust_period(10 * USEC_PER_MSEC, 100 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 1ms, quota 1000% -> keep. */ + assert_se(USEC_PER_MSEC == cgroup_cpu_adjust_period(USEC_PER_MSEC, 10000 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 100ms, quota 30% -> keep. */ + assert_se(100 * USEC_PER_MSEC == cgroup_cpu_adjust_period(100 * USEC_PER_MSEC, 300 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 5s, quota 40% -> adjust to 1s. */ + assert_se(USEC_PER_SEC == cgroup_cpu_adjust_period(5 * USEC_PER_SEC, 400 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 2s, quota 250% -> adjust to 1s. */ + assert_se(USEC_PER_SEC == cgroup_cpu_adjust_period(2 * USEC_PER_SEC, 2500 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 10us, quota 5,000,000% -> adjust to 1ms. */ + assert_se(USEC_PER_MSEC == cgroup_cpu_adjust_period(10, 50000000 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 10ms, quota 50,000% -> keep. */ + assert_se(10 * USEC_PER_MSEC == cgroup_cpu_adjust_period(10 * USEC_PER_MSEC, 500000 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 10ms, quota 1% -> adjust to 100ms. */ + assert_se(100 * USEC_PER_MSEC == cgroup_cpu_adjust_period(10 * USEC_PER_MSEC, 10 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 10ms, quota .001% -> adjust to 1s. */ + assert_se(1 * USEC_PER_SEC == cgroup_cpu_adjust_period(10 * USEC_PER_MSEC, 10, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 0ms, quota 200% -> adjust to 1ms. */ + assert_se(1 * USEC_PER_MSEC == cgroup_cpu_adjust_period(0, 2 * USEC_PER_SEC, USEC_PER_MSEC, USEC_PER_SEC)); + /* Period 0ms, quota 40% -> adjust to 2.5ms. */ + assert_se(2500 == cgroup_cpu_adjust_period(0, 400 * USEC_PER_MSEC, USEC_PER_MSEC, USEC_PER_SEC)); +} + +int main(int argc, char *argv[]) { + test_cgroup_cpu_adjust_period(); + return 0; +} diff --git a/src/test/test-chown-rec.c b/src/test/test-chown-rec.c index 305d44f56..aa11bd270 100644 --- a/src/test/test-chown-rec.c +++ b/src/test/test-chown-rec.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "chown-recursive.h" @@ -43,6 +44,8 @@ static void test_chown_recursive(void) { _cleanup_(rm_rf_physical_and_freep) char *t = NULL; struct stat st; const char *p; + const uid_t uid = getuid(); + const gid_t gid = getgid(); umask(022); assert_se(mkdtemp_malloc(NULL, &t) >= 0); @@ -52,8 +55,8 @@ static void test_chown_recursive(void) { 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(st.st_uid == uid); + assert_se(st.st_gid == gid); assert_se(!has_xattr(p)); p = strjoina(t, "/dir/symlink"); @@ -61,8 +64,8 @@ static void test_chown_recursive(void) { 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(st.st_uid == uid); + assert_se(st.st_gid == gid); assert_se(!has_xattr(p)); p = strjoina(t, "/dir/reg"); @@ -70,8 +73,8 @@ static void test_chown_recursive(void) { 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(st.st_uid == uid); + assert_se(st.st_gid == gid); assert_se(!has_xattr(p)); p = strjoina(t, "/dir/sock"); @@ -79,8 +82,8 @@ static void test_chown_recursive(void) { 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(st.st_uid == uid); + assert_se(st.st_gid == gid); assert_se(!has_xattr(p)); p = strjoina(t, "/dir/fifo"); @@ -88,8 +91,8 @@ static void test_chown_recursive(void) { 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(st.st_uid == uid); + assert_se(st.st_gid == gid); assert_se(!has_xattr(p)); /* We now apply an xattr to the dir, and check it again */ @@ -99,11 +102,11 @@ static void test_chown_recursive(void) { 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(st.st_uid == uid); + assert_se(st.st_gid == gid); assert_se(has_xattr(p)); - assert_se(path_chown_recursive(t, 1, 2) >= 0); + assert_se(path_chown_recursive(t, 1, 2, 07777) >= 0); p = strjoina(t, "/dir"); assert_se(lstat(p, &st) >= 0); diff --git a/src/test/test-condition.c b/src/test/test-condition.c index 5c2d00af8..2a5e3aba8 100644 --- a/src/test/test-condition.c +++ b/src/test/test-condition.c @@ -17,17 +17,19 @@ #include "hostname-util.h" #include "id128-util.h" #include "ima-util.h" +#include "limits-util.h" #include "log.h" #include "macro.h" +#include "nulstr-util.h" +#include "process-util.h" #include "selinux-util.h" #include "set.h" #include "smack-util.h" #include "string-util.h" #include "strv.h" +#include "tests.h" #include "tomoyo-util.h" #include "user-util.h" -#include "tests.h" -#include "util.h" #include "virt.h" static void test_condition_test_path(void) { @@ -673,6 +675,127 @@ static void test_condition_test_group(void) { condition_free(condition); } +static void test_condition_test_cpus_one(const char *s, bool result) { + Condition *condition; + int r; + + log_debug("%s=%s", condition_type_to_string(CONDITION_CPUS), s); + + condition = condition_new(CONDITION_CPUS, s, false, false); + assert_se(condition); + + r = condition_test(condition); + assert_se(r >= 0); + assert_se(r == result); + condition_free(condition); +} + +static void test_condition_test_cpus(void) { + _cleanup_free_ char *t = NULL; + int cpus; + + cpus = cpus_in_affinity_mask(); + assert_se(cpus >= 0); + + test_condition_test_cpus_one("> 0", true); + test_condition_test_cpus_one(">= 0", true); + test_condition_test_cpus_one("!= 0", true); + test_condition_test_cpus_one("<= 0", false); + test_condition_test_cpus_one("< 0", false); + test_condition_test_cpus_one("= 0", false); + + test_condition_test_cpus_one("> 100000", false); + test_condition_test_cpus_one("= 100000", false); + test_condition_test_cpus_one(">= 100000", false); + test_condition_test_cpus_one("< 100000", true); + test_condition_test_cpus_one("!= 100000", true); + test_condition_test_cpus_one("<= 100000", true); + + assert_se(asprintf(&t, "= %i", cpus) >= 0); + test_condition_test_cpus_one(t, true); + t = mfree(t); + + assert_se(asprintf(&t, "<= %i", cpus) >= 0); + test_condition_test_cpus_one(t, true); + t = mfree(t); + + assert_se(asprintf(&t, ">= %i", cpus) >= 0); + test_condition_test_cpus_one(t, true); + t = mfree(t); + + assert_se(asprintf(&t, "!= %i", cpus) >= 0); + test_condition_test_cpus_one(t, false); + t = mfree(t); + + assert_se(asprintf(&t, "< %i", cpus) >= 0); + test_condition_test_cpus_one(t, false); + t = mfree(t); + + assert_se(asprintf(&t, "> %i", cpus) >= 0); + test_condition_test_cpus_one(t, false); + t = mfree(t); +} + +static void test_condition_test_memory_one(const char *s, bool result) { + Condition *condition; + int r; + + log_debug("%s=%s", condition_type_to_string(CONDITION_MEMORY), s); + + condition = condition_new(CONDITION_MEMORY, s, false, false); + assert_se(condition); + + r = condition_test(condition); + assert_se(r >= 0); + assert_se(r == result); + condition_free(condition); +} + +static void test_condition_test_memory(void) { + _cleanup_free_ char *t = NULL; + uint64_t memory; + + memory = physical_memory(); + + test_condition_test_memory_one("> 0", true); + test_condition_test_memory_one(">= 0", true); + test_condition_test_memory_one("!= 0", true); + test_condition_test_memory_one("<= 0", false); + test_condition_test_memory_one("< 0", false); + test_condition_test_memory_one("= 0", false); + + test_condition_test_memory_one("> 18446744073709547520", false); + test_condition_test_memory_one("= 18446744073709547520", false); + test_condition_test_memory_one(">= 18446744073709547520", false); + test_condition_test_memory_one("< 18446744073709547520", true); + test_condition_test_memory_one("!= 18446744073709547520", true); + test_condition_test_memory_one("<= 18446744073709547520", true); + + assert_se(asprintf(&t, "= %" PRIu64, memory) >= 0); + test_condition_test_memory_one(t, true); + t = mfree(t); + + assert_se(asprintf(&t, "<= %" PRIu64, memory) >= 0); + test_condition_test_memory_one(t, true); + t = mfree(t); + + assert_se(asprintf(&t, ">= %" PRIu64, memory) >= 0); + test_condition_test_memory_one(t, true); + t = mfree(t); + + assert_se(asprintf(&t, "!= %" PRIu64, memory) >= 0); + test_condition_test_memory_one(t, false); + t = mfree(t); + + assert_se(asprintf(&t, "< %" PRIu64, memory) >= 0); + test_condition_test_memory_one(t, false); + t = mfree(t); + + assert_se(asprintf(&t, "> %" PRIu64, memory) >= 0); + test_condition_test_memory_one(t, false); + t = mfree(t); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -689,6 +812,8 @@ int main(int argc, char *argv[]) { test_condition_test_user(); test_condition_test_group(); test_condition_test_control_group_controller(); + test_condition_test_cpus(); + test_condition_test_memory(); return 0; } diff --git a/src/test/test-copy.c b/src/test/test-copy.c index b17a1c53f..5f4bc3958 100644 --- a/src/test/test-copy.c +++ b/src/test/test-copy.c @@ -38,7 +38,7 @@ static void test_copy_file(void) { assert_se(write_string_file(fn, "foo bar bar bar foo", WRITE_STRING_FILE_CREATE) == 0); - assert_se(copy_file(fn, fn_copy, 0, 0644, 0, COPY_REFLINK) == 0); + assert_se(copy_file(fn, fn_copy, 0, 0644, 0, 0, COPY_REFLINK) == 0); assert_se(read_full_file(fn_copy, &buf, &sz) == 0); assert_se(streq(buf, "foo bar bar bar foo\n")); @@ -246,13 +246,13 @@ static void test_copy_atomic(void) { q = strjoina(p, "/fstab"); - r = copy_file_atomic("/etc/fstab", q, 0644, 0, COPY_REFLINK); + r = copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK); if (r == -ENOENT) return; - assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, COPY_REFLINK) == -EEXIST); + assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REFLINK) == -EEXIST); - assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, COPY_REPLACE) >= 0); + assert_se(copy_file_atomic("/etc/fstab", q, 0644, 0, 0, COPY_REPLACE) >= 0); } int main(int argc, char *argv[]) { diff --git a/src/test/test-daemon.c b/src/test/test-daemon.c index a4b96da04..6ca821581 100644 --- a/src/test/test-daemon.c +++ b/src/test/test-daemon.c @@ -6,6 +6,7 @@ #include "parse-util.h" #include "strv.h" +#include "time-util.h" int main(int argc, char *argv[]) { _cleanup_strv_free_ char **l = NULL; diff --git a/src/test/test-date.c b/src/test/test-date.c index cba51e225..9888b6599 100644 --- a/src/test/test-date.c +++ b/src/test/test-date.c @@ -5,7 +5,7 @@ #include "alloc-util.h" #include "string-util.h" #include "tests.h" -#include "util.h" +#include "time-util.h" static void test_should_pass(const char *p) { usec_t t, q; diff --git a/src/test/test-engine.c b/src/test/test-engine.c index 0673d36b6..701594fe5 100644 --- a/src/test/test-engine.c +++ b/src/test/test-engine.c @@ -40,7 +40,7 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test1: (Trivial)\n"); - r = manager_add_job(m, JOB_START, c, JOB_REPLACE, &err, &j); + r = manager_add_job(m, JOB_START, c, JOB_REPLACE, NULL, &err, &j); if (sd_bus_error_is_set(&err)) log_error("error: %s: %s", err.name, err.message); assert_se(r == 0); @@ -53,15 +53,15 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test2: (Cyclic Order, Unfixable)\n"); - assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, &j) == -EDEADLK); + assert_se(manager_add_job(m, JOB_START, d, JOB_REPLACE, NULL, NULL, &j) == -EDEADLK); manager_dump_jobs(m, stdout, "\t"); printf("Test3: (Cyclic Order, Fixable, Garbage Collector)\n"); - assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, e, JOB_REPLACE, NULL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test4: (Identical transaction)\n"); - assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, e, JOB_FAIL, NULL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Load3:\n"); @@ -69,21 +69,21 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test5: (Colliding transaction, fail)\n"); - assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, &j) == -EDEADLK); + assert_se(manager_add_job(m, JOB_START, g, JOB_FAIL, NULL, NULL, &j) == -EDEADLK); printf("Test6: (Colliding transaction, replace)\n"); - assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, g, JOB_REPLACE, NULL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test7: (Unmergeable job type, fail)\n"); - assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, NULL, &j) == -EDEADLK); + assert_se(manager_add_job(m, JOB_STOP, g, JOB_FAIL, NULL, NULL, &j) == -EDEADLK); printf("Test8: (Mergeable job type, fail)\n"); - assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_RESTART, g, JOB_FAIL, NULL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Test9: (Unmergeable job type, replace)\n"); - assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_STOP, g, JOB_REPLACE, NULL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); printf("Load4:\n"); @@ -91,7 +91,7 @@ int main(int argc, char *argv[]) { manager_dump_units(m, stdout, "\t"); printf("Test10: (Unmergeable job type of auxiliary job, fail)\n"); - assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, &j) == 0); + assert_se(manager_add_job(m, JOB_START, h, JOB_FAIL, NULL, NULL, &j) == 0); manager_dump_jobs(m, stdout, "\t"); assert_se(!hashmap_get(a->dependencies[UNIT_PROPAGATES_RELOAD_TO], b)); diff --git a/src/test/test-exec-util.c b/src/test/test-exec-util.c index 21a4538d7..25ca7a2bc 100644 --- a/src/test/test-exec-util.c +++ b/src/test/test-exec-util.c @@ -117,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, NULL); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); else - execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(chdir(template_lo) == 0); assert_se(access("it_works", F_OK) >= 0); @@ -184,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, NULL); + execute_directories(dirs, DEFAULT_TIMEOUT_USEC, ignore_stdout, ignore_stdout_args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(read_full_file(output, &contents, NULL) >= 0); assert_se(streq(contents, "30-override\n80-foo\n90-bar\nlast\n")); @@ -266,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, NULL); + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_stdout, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(r >= 0); log_info("got: %s", output); @@ -332,7 +332,7 @@ static void test_environment_gathering(void) { 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); + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, NULL, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(r >= 0); STRV_FOREACH(p, env) @@ -349,7 +349,7 @@ static void test_environment_gathering(void) { env = strv_new("PATH=" DEFAULT_PATH); assert_se(env); - r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, env); + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, gather_environment, args, NULL, env, EXEC_DIR_PARALLEL | EXEC_DIR_IGNORE_ERRORS); assert_se(r >= 0); STRV_FOREACH(p, env) @@ -364,6 +364,41 @@ static void test_environment_gathering(void) { (void) setenv("PATH", old, 1); } +static void test_error_catching(void) { + char template[] = "/tmp/test-exec-util.XXXXXXX"; + const char *dirs[] = {template, NULL}; + const char *name, *name2, *name3; + int r; + + assert_se(mkdtemp(template)); + + log_info("/* %s */", __func__); + + /* write files */ + name = strjoina(template, "/10-foo"); + name2 = strjoina(template, "/20-bar"); + name3 = strjoina(template, "/30-last"); + + assert_se(write_string_file(name, + "#!/bin/sh\necho a\necho b\necho c\n", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name2, + "#!/bin/sh\nexit 42\n", + WRITE_STRING_FILE_CREATE) == 0); + assert_se(write_string_file(name3, + "#!/bin/sh\nexit 12", + WRITE_STRING_FILE_CREATE) == 0); + + assert_se(chmod(name, 0755) == 0); + assert_se(chmod(name2, 0755) == 0); + assert_se(chmod(name3, 0755) == 0); + + r = execute_directories(dirs, DEFAULT_TIMEOUT_USEC, NULL, NULL, NULL, NULL, EXEC_DIR_NONE); + + /* we should exit with the error code of the first script that failed */ + assert_se(r == 42); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -372,6 +407,7 @@ int main(int argc, char *argv[]) { test_execution_order(); test_stdout_gathering(); test_environment_gathering(); + test_error_catching(); return 0; } diff --git a/src/test/test-execute.c b/src/test/test-execute.c index eb8f7c4ef..9f1cb0ca3 100644 --- a/src/test/test-execute.c +++ b/src/test/test-execute.c @@ -33,7 +33,7 @@ static bool can_unshare; typedef void (*test_function_t)(Manager *m); -static void check(Manager *m, Unit *unit, int status_expected, int code_expected) { +static void check(const char *func, Manager *m, Unit *unit, int status_expected, int code_expected) { Service *service = NULL; usec_t ts; usec_t timeout = 2 * USEC_PER_MINUTE; @@ -62,8 +62,18 @@ static void check(Manager *m, Unit *unit, int status_expected, int code_expected } } exec_status_dump(&service->main_exec_status, stdout, "\t"); - assert_se(service->main_exec_status.status == status_expected); - assert_se(service->main_exec_status.code == code_expected); + if (service->main_exec_status.status != status_expected) { + log_error("%s: %s: exit status %d, expected %d", + func, unit->id, + service->main_exec_status.status, status_expected); + abort(); + } + if (service->main_exec_status.code != code_expected) { + log_error("%s: %s: exit code %d, expected %d", + func, unit->id, + service->main_exec_status.code, code_expected); + abort(); + } } static bool check_nobody_user_and_group(void) { @@ -148,21 +158,21 @@ static bool is_inaccessible_available(void) { return true; } -static void test(Manager *m, const char *unit_name, int status_expected, int code_expected) { +static void test(const char *func, Manager *m, const char *unit_name, int status_expected, int code_expected) { Unit *unit; assert_se(unit_name); assert_se(manager_load_startable_unit_or_warn(m, unit_name, NULL, &unit) >= 0); assert_se(unit_start(unit) >= 0); - check(m, unit, status_expected, code_expected); + check(func, m, unit, status_expected, code_expected); } 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", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, 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); @@ -180,8 +190,8 @@ static void test_exec_cpuaffinity(Manager *m) { return; } - test(m, "exec-cpuaffinity1.service", 0, CLD_EXITED); - test(m, "exec-cpuaffinity2.service", 0, CLD_EXITED); + test(__func__, m, "exec-cpuaffinity1.service", 0, CLD_EXITED); + test(__func__, m, "exec-cpuaffinity2.service", 0, CLD_EXITED); if (CPU_ISSET_S(1, CPU_ALLOC_SIZE(n), c) == 0 || CPU_ISSET_S(2, CPU_ALLOC_SIZE(n), c) == 0) { @@ -189,52 +199,52 @@ static void test_exec_cpuaffinity(Manager *m) { return; } - test(m, "exec-cpuaffinity3.service", 0, CLD_EXITED); + test(__func__, m, "exec-cpuaffinity3.service", 0, CLD_EXITED); } static void test_exec_workingdirectory(Manager *m) { assert_se(mkdir_p("/tmp/test-exec_workingdirectory", 0755) >= 0); - test(m, "exec-workingdirectory.service", 0, CLD_EXITED); - test(m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED); + test(__func__, m, "exec-workingdirectory.service", 0, CLD_EXITED); + test(__func__, m, "exec-workingdirectory-trailing-dot.service", 0, CLD_EXITED); (void) rm_rf("/tmp/test-exec_workingdirectory", REMOVE_ROOT|REMOVE_PHYSICAL); } static void test_exec_personality(Manager *m) { #if defined(__x86_64__) - test(m, "exec-personality-x86-64.service", 0, CLD_EXITED); + test(__func__, m, "exec-personality-x86-64.service", 0, CLD_EXITED); #elif defined(__s390__) - test(m, "exec-personality-s390.service", 0, CLD_EXITED); + test(__func__, m, "exec-personality-s390.service", 0, CLD_EXITED); #elif defined(__powerpc64__) # if __BYTE_ORDER == __BIG_ENDIAN - test(m, "exec-personality-ppc64.service", 0, CLD_EXITED); + test(__func__, m, "exec-personality-ppc64.service", 0, CLD_EXITED); # else - test(m, "exec-personality-ppc64le.service", 0, CLD_EXITED); + test(__func__, m, "exec-personality-ppc64le.service", 0, CLD_EXITED); # endif #elif defined(__aarch64__) - test(m, "exec-personality-aarch64.service", 0, CLD_EXITED); + test(__func__, m, "exec-personality-aarch64.service", 0, CLD_EXITED); #elif defined(__i386__) - test(m, "exec-personality-x86.service", 0, CLD_EXITED); + test(__func__, m, "exec-personality-x86.service", 0, CLD_EXITED); #else log_notice("Unknown personality, skipping %s", __func__); #endif } static void test_exec_ignoresigpipe(Manager *m) { - test(m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED); - test(m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED); + test(__func__, m, "exec-ignoresigpipe-yes.service", 0, CLD_EXITED); + test(__func__, m, "exec-ignoresigpipe-no.service", SIGPIPE, CLD_KILLED); } static void test_exec_privatetmp(Manager *m) { assert_se(touch("/tmp/test-exec_privatetmp") >= 0); - test(m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); - test(m, "exec-privatetmp-no.service", 0, CLD_EXITED); + test(__func__, m, "exec-privatetmp-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-privatetmp-no.service", 0, CLD_EXITED); unlink("/tmp/test-exec_privatetmp"); } @@ -251,9 +261,9 @@ static void test_exec_privatedevices(Manager *m) { return; } - 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", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-privatedevices-yes.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-privatedevices-no.service", 0, CLD_EXITED); + test(__func__, 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 */ @@ -263,10 +273,19 @@ static void test_exec_privatedevices(Manager *m) { return; } - test(m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED); - test(m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED); - test(m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED); - test(m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED); + test(__func__, m, "exec-privatedevices-yes-capability-mknod.service", 0, CLD_EXITED); + test(__func__, m, "exec-privatedevices-no-capability-mknod.service", 0, CLD_EXITED); + test(__func__, m, "exec-privatedevices-yes-capability-sys-rawio.service", 0, CLD_EXITED); + test(__func__, m, "exec-privatedevices-no-capability-sys-rawio.service", 0, CLD_EXITED); +} + +static void test_exec_protecthome(Manager *m) { + if (!can_unshare) { + log_notice("Cannot reliably unshare, skipping %s", __func__); + return; + } + + test(__func__, m, "exec-protecthome-tmpfs-vs-protectsystem-strict.service", 0, CLD_EXITED); } static void test_exec_protectkernelmodules(Manager *m) { @@ -287,23 +306,23 @@ static void test_exec_protectkernelmodules(Manager *m) { return; } - 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", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-protectkernelmodules-no-capabilities.service", 0, CLD_EXITED); + test(__func__, m, "exec-protectkernelmodules-yes-capabilities.service", 0, CLD_EXITED); + test(__func__, 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", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, 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", 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); + test(__func__, m, "exec-readonlypaths.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-readonlypaths-with-bindpaths.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-readonlypaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); } static void test_exec_readwritepaths(Manager *m) { @@ -313,7 +332,7 @@ static void test_exec_readwritepaths(Manager *m) { return; } - test(m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-readwritepaths-mount-propagation.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); } static void test_exec_inaccessiblepaths(Manager *m) { @@ -323,22 +342,22 @@ static void test_exec_inaccessiblepaths(Manager *m) { return; } - test(m, "exec-inaccessiblepaths-proc.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-inaccessiblepaths-sys.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", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, 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", 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); + test(__func__, m, "exec-temporaryfilesystem-options.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-temporaryfilesystem-ro.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-temporaryfilesystem-rw.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-temporaryfilesystem-usr.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); } static void test_exec_systemcallfilter(Manager *m) { @@ -350,10 +369,10 @@ static void test_exec_systemcallfilter(Manager *m) { return; } - test(m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED); - 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); + test(__func__, m, "exec-systemcallfilter-not-failing.service", 0, CLD_EXITED); + test(__func__, m, "exec-systemcallfilter-not-failing2.service", 0, CLD_EXITED); + test(__func__, m, "exec-systemcallfilter-failing.service", SIGSYS, CLD_KILLED); + test(__func__, m, "exec-systemcallfilter-failing2.service", SIGSYS, CLD_KILLED); r = find_binary("python3", NULL); if (r < 0) { @@ -361,9 +380,9 @@ static void test_exec_systemcallfilter(Manager *m) { 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); + test(__func__, m, "exec-systemcallfilter-with-errno-name.service", errno_from_name("EILSEQ"), CLD_EXITED); + test(__func__, m, "exec-systemcallfilter-with-errno-number.service", 255, CLD_EXITED); + test(__func__, m, "exec-systemcallfilter-with-errno-multi.service", errno_from_name("EILSEQ"), CLD_EXITED); #endif } @@ -382,8 +401,8 @@ static void test_exec_systemcallerrornumber(Manager *m) { return; } - test(m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED); - test(m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED); + test(__func__, m, "exec-systemcallerrornumber-name.service", errno_from_name("EACCES"), CLD_EXITED); + test(__func__, m, "exec-systemcallerrornumber-number.service", 255, CLD_EXITED); #endif } @@ -394,13 +413,13 @@ static void test_exec_restrictnamespaces(Manager *m) { return; } - 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", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); - test(m, "exec-restrictnamespaces-mnt-blacklist.service", 1, 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); + test(__func__, m, "exec-restrictnamespaces-no.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-yes.service", 1, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-mnt.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-mnt-blacklist.service", 1, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-merge-and.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-merge-or.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); + test(__func__, m, "exec-restrictnamespaces-merge-all.service", can_unshare ? 0 : EXIT_FAILURE, CLD_EXITED); #endif } @@ -411,7 +430,7 @@ static void test_exec_systemcallfilter_system(Manager *m) { return; } - test(m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED); + test(__func__, m, "exec-systemcallfilter-system-user.service", 0, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -423,12 +442,12 @@ static void test_exec_systemcallfilter_system(Manager *m) { return; } - test(m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); + test(__func__, m, "exec-systemcallfilter-system-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); #endif } static void test_exec_user(Manager *m) { - test(m, "exec-user.service", 0, CLD_EXITED); + test(__func__, m, "exec-user.service", 0, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -440,11 +459,11 @@ static void test_exec_user(Manager *m) { return; } - test(m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); + test(__func__, m, "exec-user-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); } static void test_exec_group(Manager *m) { - test(m, "exec-group.service", 0, CLD_EXITED); + test(__func__, m, "exec-group.service", 0, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -456,36 +475,36 @@ static void test_exec_group(Manager *m) { return; } - test(m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED); + test(__func__, m, "exec-group-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED); } static void test_exec_supplementarygroups(Manager *m) { - test(m, "exec-supplementarygroups.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED); - test(m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED); + test(__func__, m, "exec-supplementarygroups.service", 0, CLD_EXITED); + test(__func__, m, "exec-supplementarygroups-single-group.service", 0, CLD_EXITED); + test(__func__, m, "exec-supplementarygroups-single-group-user.service", 0, CLD_EXITED); + test(__func__, m, "exec-supplementarygroups-multiple-groups-default-group-user.service", 0, CLD_EXITED); + test(__func__, m, "exec-supplementarygroups-multiple-groups-withgid.service", 0, CLD_EXITED); + test(__func__, m, "exec-supplementarygroups-multiple-groups-withuid.service", 0, CLD_EXITED); } static void test_exec_dynamicuser(Manager *m) { - test(m, "exec-dynamicuser-fixeduser.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, 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); + test(__func__, 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); + test(__func__, m, "exec-dynamicuser-fixeduser-games.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-dynamicuser-fixeduser-one-supplementarygroup.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-dynamicuser-supplementarygroups.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, 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); (void) rm_rf("/var/lib/private/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); (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", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); + test(__func__, m, "exec-dynamicuser-statedir-migrate-step1.service", 0, CLD_EXITED); + test(__func__, m, "exec-dynamicuser-statedir-migrate-step2.service", can_unshare ? 0 : EXIT_NAMESPACE, CLD_EXITED); (void) rm_rf("/var/lib/test-dynamicuser-migrate", REMOVE_ROOT|REMOVE_PHYSICAL); (void) rm_rf("/var/lib/test-dynamicuser-migrate2", REMOVE_ROOT|REMOVE_PHYSICAL); @@ -494,9 +513,10 @@ static void test_exec_dynamicuser(Manager *m) { } static void test_exec_environment(Manager *m) { - test(m, "exec-environment.service", 0, CLD_EXITED); - test(m, "exec-environment-multiple.service", 0, CLD_EXITED); - test(m, "exec-environment-empty.service", 0, CLD_EXITED); + test(__func__, m, "exec-environment-no-substitute.service", 0, CLD_EXITED); + test(__func__, m, "exec-environment.service", 0, CLD_EXITED); + test(__func__, m, "exec-environment-multiple.service", 0, CLD_EXITED); + test(__func__, m, "exec-environment-empty.service", 0, CLD_EXITED); } static void test_exec_environmentfile(Manager *m) { @@ -516,7 +536,7 @@ static void test_exec_environmentfile(Manager *m) { r = write_string_file("/tmp/test-exec_environmentfile.conf", e, WRITE_STRING_FILE_CREATE); assert_se(r == 0); - test(m, "exec-environmentfile.service", 0, CLD_EXITED); + test(__func__, m, "exec-environmentfile.service", 0, CLD_EXITED); (void) unlink("/tmp/test-exec_environmentfile.conf"); } @@ -538,26 +558,26 @@ static void test_exec_passenvironment(Manager *m) { assert_se(setenv("VAR3", "$word 5 6", 1) == 0); assert_se(setenv("VAR4", "new\nline", 1) == 0); assert_se(setenv("VAR5", "passwordwithbackslashes", 1) == 0); - test(m, "exec-passenvironment.service", 0, CLD_EXITED); - test(m, "exec-passenvironment-repeated.service", 0, CLD_EXITED); - test(m, "exec-passenvironment-empty.service", 0, CLD_EXITED); + test(__func__, m, "exec-passenvironment.service", 0, CLD_EXITED); + test(__func__, m, "exec-passenvironment-repeated.service", 0, CLD_EXITED); + test(__func__, m, "exec-passenvironment-empty.service", 0, CLD_EXITED); assert_se(unsetenv("VAR1") == 0); assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); assert_se(unsetenv("VAR4") == 0); assert_se(unsetenv("VAR5") == 0); - test(m, "exec-passenvironment-absent.service", 0, CLD_EXITED); + test(__func__, m, "exec-passenvironment-absent.service", 0, CLD_EXITED); } static void test_exec_umask(Manager *m) { - test(m, "exec-umask-default.service", 0, CLD_EXITED); - test(m, "exec-umask-0177.service", 0, CLD_EXITED); + test(__func__, m, "exec-umask-default.service", 0, CLD_EXITED); + test(__func__, m, "exec-umask-0177.service", 0, CLD_EXITED); } static void test_exec_runtimedirectory(Manager *m) { - test(m, "exec-runtimedirectory.service", 0, CLD_EXITED); - test(m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED); - test(m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED); + test(__func__, m, "exec-runtimedirectory.service", 0, CLD_EXITED); + test(__func__, m, "exec-runtimedirectory-mode.service", 0, CLD_EXITED); + test(__func__, m, "exec-runtimedirectory-owner.service", 0, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -569,7 +589,7 @@ static void test_exec_runtimedirectory(Manager *m) { return; } - test(m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED); + test(__func__, m, "exec-runtimedirectory-owner-" NOBODY_GROUP_NAME ".service", 0, CLD_EXITED); } static void test_exec_capabilityboundingset(Manager *m) { @@ -588,14 +608,14 @@ static void test_exec_capabilityboundingset(Manager *m) { return; } - test(m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED); - test(m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED); - test(m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED); - test(m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED); + test(__func__, m, "exec-capabilityboundingset-simple.service", 0, CLD_EXITED); + test(__func__, m, "exec-capabilityboundingset-reset.service", 0, CLD_EXITED); + test(__func__, m, "exec-capabilityboundingset-merge.service", 0, CLD_EXITED); + test(__func__, m, "exec-capabilityboundingset-invert.service", 0, CLD_EXITED); } static void test_exec_basic(Manager *m) { - test(m, "exec-basic.service", 0, CLD_EXITED); + test(__func__, m, "exec-basic.service", 0, CLD_EXITED); } static void test_exec_ambientcapabilities(Manager *m) { @@ -617,8 +637,8 @@ static void test_exec_ambientcapabilities(Manager *m) { return; } - test(m, "exec-ambientcapabilities.service", 0, CLD_EXITED); - test(m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED); + test(__func__, m, "exec-ambientcapabilities.service", 0, CLD_EXITED); + test(__func__, m, "exec-ambientcapabilities-merge.service", 0, CLD_EXITED); if (!check_nobody_user_and_group()) { log_notice("nobody user/group is not synthesized or may conflict to other entries, skipping remaining tests in %s", __func__); @@ -630,8 +650,8 @@ static void test_exec_ambientcapabilities(Manager *m) { return; } - test(m, "exec-ambientcapabilities-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); - test(m, "exec-ambientcapabilities-merge-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); + test(__func__, m, "exec-ambientcapabilities-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); + test(__func__, m, "exec-ambientcapabilities-merge-" NOBODY_USER_NAME ".service", 0, CLD_EXITED); } static void test_exec_privatenetwork(Manager *m) { @@ -643,56 +663,63 @@ static void test_exec_privatenetwork(Manager *m) { return; } - test(m, "exec-privatenetwork-yes.service", can_unshare ? 0 : EXIT_NETWORK, CLD_EXITED); + test(__func__, 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); + test(__func__, 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); + test(__func__, 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-best-effort.service", 0, CLD_EXITED); + test(__func__, m, "exec-ioschedulingclass-none.service", 0, CLD_EXITED); + test(__func__, m, "exec-ioschedulingclass-idle.service", 0, CLD_EXITED); + test(__func__, 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); + test(__func__, m, "exec-ioschedulingclass-realtime.service", 0, CLD_EXITED); } static void test_exec_unsetenvironment(Manager *m) { - test(m, "exec-unsetenvironment.service", 0, CLD_EXITED); + test(__func__, m, "exec-unsetenvironment.service", 0, CLD_EXITED); } static void test_exec_specifier(Manager *m) { - test(m, "exec-specifier.service", 0, CLD_EXITED); - test(m, "exec-specifier@foo-bar.service", 0, CLD_EXITED); - test(m, "exec-specifier-interpolation.service", 0, CLD_EXITED); + test(__func__, m, "exec-specifier.service", 0, CLD_EXITED); + test(__func__, m, "exec-specifier@foo-bar.service", 0, CLD_EXITED); + test(__func__, m, "exec-specifier-interpolation.service", 0, CLD_EXITED); } static void test_exec_standardinput(Manager *m) { - test(m, "exec-standardinput-data.service", 0, CLD_EXITED); - test(m, "exec-standardinput-file.service", 0, CLD_EXITED); + test(__func__, m, "exec-standardinput-data.service", 0, CLD_EXITED); + test(__func__, m, "exec-standardinput-file.service", 0, CLD_EXITED); } static void test_exec_standardoutput(Manager *m) { - test(m, "exec-standardoutput-file.service", 0, CLD_EXITED); + test(__func__, 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); + test(__func__, 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; +typedef struct test_entry { + test_function_t f; + const char *name; +} test_entry; + +#define entry(x) {x, #x} + +static int run_tests(UnitFileScope scope, const test_entry tests[], char **patterns) { + const test_entry *test = NULL; _cleanup_(manager_freep) Manager *m = NULL; int r; @@ -704,57 +731,62 @@ static int run_tests(UnitFileScope scope, const test_function_t *tests) { assert_se(r >= 0); assert_se(manager_startup(m, NULL, NULL) >= 0); - for (test = tests; test && *test; test++) - (*test)(m); + for (test = tests; test && test->f; test++) + if (strv_fnmatch_or_empty(patterns, test->name, FNM_NOESCAPE)) + test->f(m); + else + log_info("Skipping %s because it does not match any pattern.", test->name); return 0; } + 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, - test_exec_bindpaths, - test_exec_capabilityboundingset, - test_exec_cpuaffinity, - test_exec_environment, - test_exec_environmentfile, - test_exec_group, - test_exec_ignoresigpipe, - test_exec_inaccessiblepaths, - test_exec_ioschedulingclass, - test_exec_oomscoreadjust, - test_exec_passenvironment, - test_exec_personality, - test_exec_privatedevices, - test_exec_privatenetwork, - test_exec_privatetmp, - test_exec_protectkernelmodules, - test_exec_readonlypaths, - test_exec_readwritepaths, - 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, - test_exec_temporaryfilesystem, - test_exec_umask, - test_exec_unsetenvironment, - test_exec_user, - test_exec_workingdirectory, - NULL, + + static const test_entry user_tests[] = { + entry(test_exec_basic), + entry(test_exec_ambientcapabilities), + entry(test_exec_bindpaths), + entry(test_exec_capabilityboundingset), + entry(test_exec_cpuaffinity), + entry(test_exec_environment), + entry(test_exec_environmentfile), + entry(test_exec_group), + entry(test_exec_ignoresigpipe), + entry(test_exec_inaccessiblepaths), + entry(test_exec_ioschedulingclass), + entry(test_exec_oomscoreadjust), + entry(test_exec_passenvironment), + entry(test_exec_personality), + entry(test_exec_privatedevices), + entry(test_exec_privatenetwork), + entry(test_exec_privatetmp), + entry(test_exec_protecthome), + entry(test_exec_protectkernelmodules), + entry(test_exec_readonlypaths), + entry(test_exec_readwritepaths), + entry(test_exec_restrictnamespaces), + entry(test_exec_runtimedirectory), + entry(test_exec_standardinput), + entry(test_exec_standardoutput), + entry(test_exec_standardoutput_append), + entry(test_exec_supplementarygroups), + entry(test_exec_systemcallerrornumber), + entry(test_exec_systemcallfilter), + entry(test_exec_temporaryfilesystem), + entry(test_exec_umask), + entry(test_exec_unsetenvironment), + entry(test_exec_user), + entry(test_exec_workingdirectory), + {}, }; - static const test_function_t system_tests[] = { - test_exec_dynamicuser, - test_exec_specifier, - test_exec_systemcallfilter_system, - NULL, + static const test_entry system_tests[] = { + entry(test_exec_dynamicuser), + entry(test_exec_specifier), + entry(test_exec_systemcallfilter_system), + {}, }; int r; @@ -796,11 +828,11 @@ int main(int argc, char *argv[]) { assert_se(unsetenv("VAR2") == 0); assert_se(unsetenv("VAR3") == 0); - r = run_tests(UNIT_FILE_USER, user_tests); + r = run_tests(UNIT_FILE_USER, user_tests, argv + 1); if (r != 0) return r; - r = run_tests(UNIT_FILE_SYSTEM, system_tests); + r = run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); if (r != 0) return r; @@ -811,6 +843,7 @@ int main(int argc, char *argv[]) { return 0; } + _cleanup_hashmap_free_ Hashmap *s = NULL; assert_se(s = hashmap_new(NULL)); r = seccomp_syscall_resolve_name("unshare"); assert_se(r != __NR_SCMP_ERROR); @@ -821,11 +854,11 @@ int main(int argc, char *argv[]) { can_unshare = false; - r = run_tests(UNIT_FILE_USER, user_tests); + r = run_tests(UNIT_FILE_USER, user_tests, argv + 1); if (r != 0) return r; - return run_tests(UNIT_FILE_SYSTEM, system_tests); + return run_tests(UNIT_FILE_SYSTEM, system_tests, argv + 1); #else return 0; #endif diff --git a/src/test/test-fd-util.c b/src/test/test-fd-util.c index 7a0a2ad7d..713795814 100644 --- a/src/test/test-fd-util.c +++ b/src/test/test-fd-util.c @@ -7,6 +7,7 @@ #include "fd-util.h" #include "fileio.h" #include "macro.h" +#include "memory-util.h" #include "path-util.h" #include "process-util.h" #include "random-util.h" @@ -14,7 +15,6 @@ #include "string-util.h" #include "tests.h" #include "tmpfile-util.h" -#include "util.h" static void test_close_many(void) { int fds[3]; diff --git a/src/test/test-fileio.c b/src/test/test-fileio.c index 2ddaabe7f..2ff5b9a69 100644 --- a/src/test/test-fileio.c +++ b/src/test/test-fileio.c @@ -629,6 +629,10 @@ static void test_tempfn(void) { static const char chars[] = "Aąę„”\n루\377"; + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" + static void test_fgetc(void) { _cleanup_fclose_ FILE *f = NULL; char c; @@ -640,7 +644,7 @@ static void test_fgetc(void) { 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 */ + /* EOF is -1, and hence we can't push value 255 in this way if char is signed */ assert_se(ungetc(c, f) != EOF || c == EOF); assert_se(c == EOF || safe_fgetc(f, &c) == 1); assert_se(c == chars[i]); @@ -654,6 +658,8 @@ static void test_fgetc(void) { assert_se(safe_fgetc(f, &c) == 0); } +#pragma GCC diagnostic pop + static const char buffer[] = "Some test data\n" "루Non-ascii chars: ąę„”\n" @@ -753,7 +759,11 @@ static void test_read_line3(void) { assert_se(f); r = read_line(f, LINE_MAX, &line); - assert_se((size_t) r == strlen(line) + 1); + assert_se(r >= 0); + if (r == 0) + assert_se(line && isempty(line)); + else + assert_se((size_t) r == strlen(line) + 1); assert_se(read_line(f, LINE_MAX, NULL) == 0); } diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index e049abc4a..f0f015056 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -154,6 +154,30 @@ static void test_chase_symlinks(void) { assert_se(path_equal(result, q)); result = mfree(result); + /* Paths underneath the "root" with different UIDs while using CHASE_SAFE */ + + if (geteuid() == 0) { + p = strjoina(temp, "/user"); + assert_se(mkdir(p, 0755) >= 0); + assert_se(chown(p, UID_NOBODY, GID_NOBODY) >= 0); + + q = strjoina(temp, "/user/root"); + assert_se(mkdir(q, 0755) >= 0); + + p = strjoina(q, "/link"); + assert_se(symlink("/", p) >= 0); + + /* Fail when user-owned directories contain root-owned subdirectories. */ + r = chase_symlinks(p, temp, CHASE_SAFE, &result); + assert_se(r == -ENOLINK); + result = mfree(result); + + /* Allow this when the user-owned directories are all in the "root". */ + r = chase_symlinks(p, q, CHASE_SAFE, &result); + assert_se(r > 0); + result = mfree(result); + } + /* Paths using . */ r = chase_symlinks("/etc/./.././", NULL, 0, &result); diff --git a/src/test/test-fstab-util.c b/src/test/test-fstab-util.c index bd873a39b..4cd504e45 100644 --- a/src/test/test-fstab-util.c +++ b/src/test/test-fstab-util.c @@ -1,10 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "fstab-util.h" #include "log.h" #include "string-util.h" -#include "util.h" /* int fstab_filter_options(const char *opts, const char *names, diff --git a/src/test/test-hashmap-plain.c b/src/test/test-hashmap-plain.c index 5376aa84c..fdb0c7e87 100644 --- a/src/test/test-hashmap-plain.c +++ b/src/test/test-hashmap-plain.c @@ -3,11 +3,12 @@ #include "alloc-util.h" #include "hashmap.h" #include "log.h" +#include "nulstr-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "time-util.h" #include "tests.h" -#include "util.h" void test_hashmap_funcs(void); diff --git a/src/test/test-hostname-util.c b/src/test/test-hostname-util.c index 4126a24ce..ec34f9cd7 100644 --- a/src/test/test-hostname-util.c +++ b/src/test/test-hostname-util.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "fileio.h" #include "hostname-util.h" diff --git a/src/test/test-id128.c b/src/test/test-id128.c index ec50e057a..884078831 100644 --- a/src/test/test-id128.c +++ b/src/test/test-id128.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include +#include +#include #include "sd-daemon.h" #include "sd-id128.h" diff --git a/src/test/test-in-addr-util.c b/src/test/test-in-addr-util.c index 16844e956..916bd35f0 100644 --- a/src/test/test-in-addr-util.c +++ b/src/test/test-in-addr-util.c @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include "log.h" +#include "strv.h" #include "in-addr-util.h" -static void test_in_addr_prefix_from_string( +static void test_in_addr_prefix_from_string_one( const char *p, int family, int ret, @@ -55,32 +57,130 @@ static void test_in_addr_prefix_from_string( } } -int main(int argc, char *argv[]) { - test_in_addr_prefix_from_string("", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); - test_in_addr_prefix_from_string("/", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); - test_in_addr_prefix_from_string("/8", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 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, -ENOANO, 0, 0, 8); - 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, 0, 0, 0, 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, 0, 1, 0, 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, 0, 2, 0, 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, 0, 32, 0, 32); - test_in_addr_prefix_from_string("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); - test_in_addr_prefix_from_string("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); - test_in_addr_prefix_from_string("::1", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); +static void test_in_addr_prefix_from_string(void) { + test_in_addr_prefix_from_string_one("", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); + test_in_addr_prefix_from_string_one("/", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); + test_in_addr_prefix_from_string_one("/8", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); + test_in_addr_prefix_from_string_one("1.2.3.4", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, -ENOANO, 0, 0, 8); + test_in_addr_prefix_from_string_one("1.2.3.4/0", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 0, 0, 0, 0, 0); + test_in_addr_prefix_from_string_one("1.2.3.4/1", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 1, 0, 1, 0, 1); + test_in_addr_prefix_from_string_one("1.2.3.4/2", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 2, 0, 2, 0, 2); + test_in_addr_prefix_from_string_one("1.2.3.4/32", AF_INET, 0, &(union in_addr_union) { .in = (struct in_addr) { .s_addr = htobe32(0x01020304) } }, 32, 0, 32, 0, 32); + test_in_addr_prefix_from_string_one("1.2.3.4/33", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); + test_in_addr_prefix_from_string_one("1.2.3.4/-1", AF_INET, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); + test_in_addr_prefix_from_string_one("::1", AF_INET, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); - test_in_addr_prefix_from_string("", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); - test_in_addr_prefix_from_string("/", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); - test_in_addr_prefix_from_string("/8", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); - test_in_addr_prefix_from_string("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, -ENOANO, 0, 0, 0); - test_in_addr_prefix_from_string("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, 0, 0, 0, 0); - test_in_addr_prefix_from_string("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1, 0, 1, 0, 1); - test_in_addr_prefix_from_string("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2, 0, 2, 0, 2); - test_in_addr_prefix_from_string("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32, 0, 32, 0, 32); - test_in_addr_prefix_from_string("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33, 0, 33, 0, 33); - test_in_addr_prefix_from_string("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64, 0, 64, 0, 64); - test_in_addr_prefix_from_string("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, 0, 128, 0, 128); - test_in_addr_prefix_from_string("::1/129", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); - test_in_addr_prefix_from_string("::1/-1", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); + test_in_addr_prefix_from_string_one("", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); + test_in_addr_prefix_from_string_one("/", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); + test_in_addr_prefix_from_string_one("/8", AF_INET6, -EINVAL, NULL, 0, -EINVAL, 0, -EINVAL, 0); + test_in_addr_prefix_from_string_one("::1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, -ENOANO, 0, 0, 0); + test_in_addr_prefix_from_string_one("::1/0", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 0, 0, 0, 0, 0); + test_in_addr_prefix_from_string_one("::1/1", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 1, 0, 1, 0, 1); + test_in_addr_prefix_from_string_one("::1/2", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 2, 0, 2, 0, 2); + test_in_addr_prefix_from_string_one("::1/32", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 32, 0, 32, 0, 32); + test_in_addr_prefix_from_string_one("::1/33", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 33, 0, 33, 0, 33); + test_in_addr_prefix_from_string_one("::1/64", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 64, 0, 64, 0, 64); + test_in_addr_prefix_from_string_one("::1/128", AF_INET6, 0, &(union in_addr_union) { .in6 = IN6ADDR_LOOPBACK_INIT }, 128, 0, 128, 0, 128); + test_in_addr_prefix_from_string_one("::1/129", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); + test_in_addr_prefix_from_string_one("::1/-1", AF_INET6, -ERANGE, NULL, 0, -ERANGE, 0, -ERANGE, 0); +} + +static void test_in_addr_prefix_to_string_valid(int family, const char *p) { + _cleanup_free_ char *str = NULL; + union in_addr_union u; + unsigned char l; + + log_info("/* %s */", p); + + assert_se(in_addr_prefix_from_string(p, family, &u, &l) >= 0); + assert_se(in_addr_prefix_to_string(family, &u, l, &str) >= 0); + assert_se(streq(str, p)); +} + +static void test_in_addr_prefix_to_string_unoptimized(int family, const char *p) { + _cleanup_free_ char *str1 = NULL, *str2 = NULL; + union in_addr_union u1, u2; + unsigned char len1, len2; + + log_info("/* %s */", p); + + assert_se(in_addr_prefix_from_string(p, family, &u1, &len1) >= 0); + assert_se(in_addr_prefix_to_string(family, &u1, len1, &str1) >= 0); + assert_se(in_addr_prefix_from_string(str1, family, &u2, &len2) >= 0); + assert_se(in_addr_prefix_to_string(family, &u2, len2, &str2) >= 0); + + assert_se(streq(str1, str2)); + assert_se(len1 == len2); + assert_se(in_addr_equal(family, &u1, &u2) > 0); +} + +static void test_in_addr_prefix_to_string(void) { + test_in_addr_prefix_to_string_valid(AF_INET, "0.0.0.0/32"); + test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/0"); + test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/24"); + test_in_addr_prefix_to_string_valid(AF_INET, "1.2.3.4/32"); + test_in_addr_prefix_to_string_valid(AF_INET, "255.255.255.255/32"); + + test_in_addr_prefix_to_string_valid(AF_INET6, "::1/128"); + test_in_addr_prefix_to_string_valid(AF_INET6, "fd00:abcd::1/64"); + test_in_addr_prefix_to_string_valid(AF_INET6, "fd00:abcd::1234:1/64"); + test_in_addr_prefix_to_string_valid(AF_INET6, "1111:2222:3333:4444:5555:6666:7777:8888/128"); + + test_in_addr_prefix_to_string_unoptimized(AF_INET, "0.0.0.0"); + test_in_addr_prefix_to_string_unoptimized(AF_INET, "192.168.0.1"); + + test_in_addr_prefix_to_string_unoptimized(AF_INET6, "fd00:0000:0000:0000:0000:0000:0000:0001/64"); + test_in_addr_prefix_to_string_unoptimized(AF_INET6, "fd00:1111::0000:2222:3333:4444:0001/64"); +} + +static void test_in_addr_random_prefix(void) { + _cleanup_free_ char *str = NULL; + union in_addr_union a; + + assert_se(in_addr_from_string(AF_INET, "192.168.10.1", &a) >= 0); + + assert_se(in_addr_random_prefix(AF_INET, &a, 31, 32) >= 0); + assert_se(in_addr_to_string(AF_INET, &a, &str) >= 0); + assert_se(STR_IN_SET(str, "192.168.10.0", "192.168.10.1")); + str = mfree(str); + + assert_se(in_addr_random_prefix(AF_INET, &a, 24, 26) >= 0); + assert_se(in_addr_to_string(AF_INET, &a, &str) >= 0); + assert_se(startswith(str, "192.168.10.")); + str = mfree(str); + + assert_se(in_addr_random_prefix(AF_INET, &a, 16, 24) >= 0); + assert_se(in_addr_to_string(AF_INET, &a, &str) >= 0); + assert_se(fnmatch("192.168.[0-9]*.0", str, 0) == 0); + str = mfree(str); + + assert_se(in_addr_random_prefix(AF_INET, &a, 8, 24) >= 0); + assert_se(in_addr_to_string(AF_INET, &a, &str) >= 0); + assert_se(fnmatch("192.[0-9]*.[0-9]*.0", str, 0) == 0); + str = mfree(str); + + assert_se(in_addr_random_prefix(AF_INET, &a, 8, 16) >= 0); + assert_se(in_addr_to_string(AF_INET, &a, &str) >= 0); + assert_se(fnmatch("192.[0-9]*.0.0", str, 0) == 0); + str = mfree(str); + + assert_se(in_addr_from_string(AF_INET6, "fd00::1", &a) >= 0); + + assert_se(in_addr_random_prefix(AF_INET6, &a, 16, 64) >= 0); + assert_se(in_addr_to_string(AF_INET6, &a, &str) >= 0); + assert_se(startswith(str, "fd00:")); + str = mfree(str); + + assert_se(in_addr_random_prefix(AF_INET6, &a, 8, 16) >= 0); + assert_se(in_addr_to_string(AF_INET6, &a, &str) >= 0); + assert_se(fnmatch("fd??::", str, 0) == 0); + str = mfree(str); +} + +int main(int argc, char *argv[]) { + test_in_addr_prefix_from_string(); + test_in_addr_random_prefix(); + test_in_addr_prefix_to_string(); return 0; } diff --git a/src/test/test-install-root.c b/src/test/test-install-root.c index 73ea68f37..cfddfe19a 100644 --- a/src/test/test-install-root.c +++ b/src/test/test-install-root.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "alloc-util.h" #include "fileio.h" #include "install.h" diff --git a/src/test/test-json.c b/src/test/test-json.c index fdf1b4f40..9b8a2a942 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -8,6 +8,7 @@ #include "json.h" #include "string-util.h" #include "strv.h" +#include "tests.h" #include "util.h" static void test_tokenizer(const char *data, ...) { @@ -391,6 +392,13 @@ static void test_depth(void) { log_info("max depth at %u", i); break; } +#if HAS_FEATURE_MEMORY_SANITIZER + /* msan doesn't like the stack nesting to be too deep. Let's quit early. */ + if (i >= 128) { + log_info("quitting early at depth %u", i); + break; + } +#endif assert_se(r >= 0); @@ -403,10 +411,7 @@ static void test_depth(void) { } int main(int argc, char *argv[]) { - - log_set_max_level(LOG_DEBUG); - log_parse_environment(); - log_open(); + test_setup_logging(LOG_DEBUG); test_tokenizer("x", -EINVAL); test_tokenizer("", JSON_TOKEN_END); diff --git a/src/test/test-libmount.c b/src/test/test-libmount.c new file mode 100644 index 000000000..fc28f27d5 --- /dev/null +++ b/src/test/test-libmount.c @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "alloc-util.h" +#include "fd-util.h" +#include "escape.h" +#include "libmount-util.h" +#include "tests.h" + +static void test_libmount_unescaping_one( + const char *title, + const char *string, + bool may_fail, + const char *expected_source, + const char *expected_target) { + /* A test for libmount really */ + int r; + + log_info("/* %s %s */", __func__, title); + + _cleanup_(mnt_free_tablep) struct libmnt_table *table = NULL; + _cleanup_(mnt_free_iterp) struct libmnt_iter *iter = NULL; + _cleanup_fclose_ FILE *f = NULL; + + assert_se(table = mnt_new_table()); + assert_se(iter = mnt_new_iter(MNT_ITER_FORWARD)); + + f = fmemopen((char*) string, strlen(string), "re"); + assert_se(f); + + assert_se(mnt_table_parse_stream(table, f, title) >= 0); + + struct libmnt_fs *fs; + const char *source, *target; + _cleanup_free_ char *x = NULL, *cs = NULL, *s = NULL, *ct = NULL, *t = NULL; + + /* We allow this call and the checks below to fail in some cases. See the case definitions below. */ + + r = mnt_table_next_fs(table, iter, &fs); + if (r != 0 && may_fail) { + log_error_errno(r, "mnt_table_next_fs failed: %m"); + return; + } + assert_se(r == 0); + + assert_se(x = cescape(string)); + + assert_se(source = mnt_fs_get_source(fs)); + assert_se(target = mnt_fs_get_target(fs)); + + assert_se(cs = cescape(source)); + assert_se(ct = cescape(target)); + + assert_se(cunescape(source, UNESCAPE_RELAX, &s) >= 0); + assert_se(cunescape(target, UNESCAPE_RELAX, &t) >= 0); + + log_info("from '%s'", x); + log_info("source: '%s'", source); + log_info("source: '%s'", cs); + log_info("source: '%s'", s); + log_info("expected: '%s'", strna(expected_source)); + log_info("target: '%s'", target); + log_info("target: '%s'", ct); + log_info("target: '%s'", t); + log_info("expected: '%s'", strna(expected_target)); + + assert_se(may_fail || streq(source, expected_source)); + assert_se(may_fail || streq(target, expected_target)); + + assert_se(mnt_table_next_fs(table, iter, &fs) == 1); +} + +static void test_libmount_unescaping(void) { + test_libmount_unescaping_one( + "escaped space + utf8", + "729 38 0:59 / /tmp/„zupa\\040zębowa” rw,relatime shared:395 - tmpfs die\\040Brühe rw,seclabel", + false, + "die Brühe", + "/tmp/„zupa zębowa”" + ); + + test_libmount_unescaping_one( + "escaped newline", + "729 38 0:59 / /tmp/x\\012y rw,relatime shared:395 - tmpfs newline rw,seclabel", + false, + "newline", + "/tmp/x\ny" + ); + + /* The result of "mount -t tmpfs '' /tmp/emptysource". + * This will fail with libmount <= v2.33. + * See https://github.com/karelzak/util-linux/commit/18a52a5094. + */ + test_libmount_unescaping_one( + "empty source", + "760 38 0:60 / /tmp/emptysource rw,relatime shared:410 - tmpfs rw,seclabel", + true, + "", + "/tmp/emptysource" + ); + + /* The kernel leaves \r as is. + * Also see https://github.com/karelzak/util-linux/issues/780. + */ + test_libmount_unescaping_one( + "foo\\rbar", + "790 38 0:61 / /tmp/foo\rbar rw,relatime shared:425 - tmpfs tmpfs rw,seclabel", + true, + "tmpfs", + "/tmp/foo\rbar" + ); +} + +int main(int argc, char *argv[]) { + test_setup_logging(LOG_DEBUG); + + test_libmount_unescaping(); + return 0; +} diff --git a/src/test/test-libudev.c b/src/test/test-libudev.c index 15c0f8853..f634ca28d 100644 --- a/src/test/test-libudev.c +++ b/src/test/test-libudev.c @@ -12,8 +12,12 @@ #include "libudev-list-internal.h" #include "libudev-util.h" #include "log.h" +#include "main-func.h" #include "stdio-util.h" #include "string-util.h" +#include "tests.h" + +static bool arg_monitor = false; static void print_device(struct udev_device *device) { const char *str; @@ -23,7 +27,7 @@ static void print_device(struct udev_device *device) { log_info("*** device: %p ***", device); str = udev_device_get_action(device); - if (str != NULL) + if (str) log_info("action: '%s'", str); str = udev_device_get_syspath(device); @@ -33,26 +37,26 @@ static void print_device(struct udev_device *device) { log_info("sysname: '%s'", str); str = udev_device_get_sysnum(device); - if (str != NULL) + if (str) log_info("sysnum: '%s'", str); str = udev_device_get_devpath(device); log_info("devpath: '%s'", str); str = udev_device_get_subsystem(device); - if (str != NULL) + if (str) log_info("subsystem: '%s'", str); str = udev_device_get_devtype(device); - if (str != NULL) + if (str) log_info("devtype: '%s'", str); str = udev_device_get_driver(device); - if (str != NULL) + if (str) log_info("driver: '%s'", str); str = udev_device_get_devnode(device); - if (str != NULL) + if (str) log_info("devname: '%s'", str); devnum = udev_device_get_devnum(device); @@ -78,30 +82,30 @@ static void print_device(struct udev_device *device) { log_info("found %i properties", count); str = udev_device_get_property_value(device, "MAJOR"); - if (str != NULL) + if (str) log_info("MAJOR: '%s'", str); str = udev_device_get_sysattr_value(device, "dev"); - if (str != NULL) + if (str) log_info("attr{dev}: '%s'", str); } static void test_device(struct udev *udev, const char *syspath) { _cleanup_(udev_device_unrefp) struct udev_device *device; - log_info("looking at device: %s", syspath); + log_info("/* %s, device %s */", __func__, syspath); device = udev_device_new_from_syspath(udev, syspath); - if (device == NULL) - log_warning_errno(errno, "udev_device_new_from_syspath: %m"); - else + if (device) print_device(device); + else + log_warning_errno(errno, "udev_device_new_from_syspath: %m"); } static void test_device_parents(struct udev *udev, const char *syspath) { _cleanup_(udev_device_unrefp) struct udev_device *device; struct udev_device *device_parent; - log_info("looking at device: %s", syspath); + log_info("/* %s, device %s */", __func__, syspath); device = udev_device_new_from_syspath(udev, syspath); if (device == NULL) return; @@ -125,12 +129,13 @@ static void test_device_devnum(struct udev *udev) { dev_t devnum = makedev(1, 3); _cleanup_(udev_device_unrefp) struct udev_device *device; - log_info("looking up device: %u:%u", major(devnum), minor(devnum)); + log_info("/* %s, device %d:%d */", __func__, major(devnum), minor(devnum)); + device = udev_device_new_from_devnum(udev, 'c', devnum); - if (device == NULL) - log_warning_errno(errno, "udev_device_new_from_devnum: %m"); - else + if (device) print_device(device); + else + log_warning_errno(errno, "udev_device_new_from_devnum: %m"); } static void test_device_subsys_name(struct udev *udev, const char *subsys, const char *dev) { @@ -144,7 +149,7 @@ static void test_device_subsys_name(struct udev *udev, const char *subsys, const print_device(device); } -static int test_enumerate_print_list(struct udev_enumerate *enumerate) { +static int enumerate_print_list(struct udev_enumerate *enumerate) { struct udev_list_entry *list_entry; int count = 0; @@ -176,6 +181,8 @@ static void test_monitor(struct udev *udev) { .data.fd = STDIN_FILENO, }; + log_info("/* %s */", __func__); + fd_ep = epoll_create1(EPOLL_CLOEXEC); assert_se(fd_ep >= 0); @@ -225,8 +232,9 @@ static void test_queue(struct udev *udev) { struct udev_queue *udev_queue; bool empty; - udev_queue = udev_queue_new(udev); - assert_se(udev_queue); + log_info("/* %s */", __func__); + + assert_se(udev_queue = udev_queue_new(udev)); empty = udev_queue_get_queue_is_empty(udev_queue); log_info("queue is %s", empty ? "empty" : "not empty"); @@ -237,13 +245,15 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { struct udev_enumerate *udev_enumerate; int r; + log_info("/* %s */", __func__); + log_info("enumerate '%s'", subsystem == NULL ? "" : subsystem); udev_enumerate = udev_enumerate_new(udev); if (udev_enumerate == NULL) return -1; udev_enumerate_add_match_subsystem(udev_enumerate, subsystem); udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); log_info("enumerate 'net' + duplicated scan + null + zero"); @@ -263,7 +273,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); udev_enumerate_add_syspath(udev_enumerate, "/sys/class/mem/zero"); udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); log_info("enumerate 'block'"); @@ -277,7 +287,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { return r; } udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); log_info("enumerate 'not block'"); @@ -286,7 +296,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { return -1; udev_enumerate_add_nomatch_subsystem(udev_enumerate, "block"); udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); log_info("enumerate 'pci, mem, vc'"); @@ -297,7 +307,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { udev_enumerate_add_match_subsystem(udev_enumerate, "mem"); udev_enumerate_add_match_subsystem(udev_enumerate, "vc"); udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); log_info("enumerate 'subsystem'"); @@ -305,7 +315,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { if (udev_enumerate == NULL) return -1; udev_enumerate_scan_subsystems(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); log_info("enumerate 'property IF_FS_*=filesystem'"); @@ -314,7 +324,7 @@ static int test_enumerate(struct udev *udev, const char *subsystem) { return -1; udev_enumerate_add_match_property(udev_enumerate, "ID_FS*", "filesystem"); udev_enumerate_scan_devices(udev_enumerate); - test_enumerate_print_list(udev_enumerate); + enumerate_print_list(udev_enumerate); udev_enumerate_unref(udev_enumerate); return 0; } @@ -323,7 +333,11 @@ static void test_hwdb(struct udev *udev, const char *modalias) { struct udev_hwdb *hwdb; struct udev_list_entry *entry; + log_info("/* %s */", __func__); + hwdb = udev_hwdb_new(udev); + if (!hwdb) + log_warning_errno(errno, "Failed to open hwdb: %m"); udev_list_entry_foreach(entry, udev_hwdb_get_properties_list_entry(hwdb, modalias, 0)) log_info("'%s'='%s'", udev_list_entry_get_name(entry), udev_list_entry_get_value(entry)); @@ -348,6 +362,8 @@ static void test_util_replace_whitespace_one(const char *str, const char *expect } static void test_util_replace_whitespace(void) { + log_info("/* %s */", __func__); + 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"); @@ -385,16 +401,19 @@ static void test_util_replace_whitespace(void) { } static void test_util_resolve_subsys_kernel_one(const char *str, bool read_value, int retval, const char *expected) { - char result[UTIL_PATH_SIZE]; + char result[UTIL_PATH_SIZE] = ""; int r; r = util_resolve_subsys_kernel(str, result, sizeof(result), read_value); + log_info("\"%s\" → expect: \"%s\", %d, actual: \"%s\", %d", str, strnull(expected), retval, result, r); assert_se(r == retval); if (r >= 0) assert_se(streq(result, expected)); } static void test_util_resolve_subsys_kernel(void) { + log_info("/* %s */", __func__); + 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); @@ -471,9 +490,7 @@ static void test_list(void) { udev_list_cleanup(&list); } -int main(int argc, char *argv[]) { - _cleanup_(udev_unrefp) struct udev *udev = NULL; - bool arg_monitor = false; +static int parse_args(int argc, char *argv[], const char **syspath, const char **subsystem) { static const struct option options[] = { { "syspath", required_argument, NULL, 'p' }, { "subsystem", required_argument, NULL, 's' }, @@ -483,52 +500,59 @@ int main(int argc, char *argv[]) { { "monitor", no_argument, NULL, 'm' }, {} }; - const char *syspath = "/devices/virtual/mem/null"; - const char *subsystem = NULL; int c; - udev = udev_new(); - log_info("context: %p", udev); - if (udev == NULL) { - log_info("no context"); - return 1; - } - while ((c = getopt_long(argc, argv, "p:s:dhVm", options, NULL)) >= 0) switch (c) { - case 'p': - syspath = optarg; + *syspath = optarg; break; case 's': - subsystem = optarg; + *subsystem = optarg; break; case 'd': - if (log_get_max_level() < LOG_INFO) - log_set_max_level(LOG_INFO); + log_set_max_level(LOG_DEBUG); break; case 'h': printf("--debug --syspath= --subsystem= --help\n"); - return EXIT_SUCCESS; + return 0; case 'V': printf("%s\n", GIT_VERSION); - return EXIT_SUCCESS; + return 0; case 'm': arg_monitor = true; break; case '?': - return EXIT_FAILURE; + return -EINVAL; default: assert_not_reached("Unhandled option code."); } + return 1; +} + +static int run(int argc, char *argv[]) { + _cleanup_(udev_unrefp) struct udev *udev = NULL; + + const char *syspath = "/devices/virtual/mem/null"; + const char *subsystem = NULL; + int r; + + test_setup_logging(LOG_INFO); + + r = parse_args(argc, argv, &syspath, &subsystem); + if (r <= 0) + return r; + + assert_se(udev = udev_new()); + /* add sys path if needed */ if (!startswith(syspath, "/sys")) syspath = strjoina("/sys/", syspath); @@ -555,5 +579,7 @@ int main(int argc, char *argv[]) { test_list(); - return EXIT_SUCCESS; + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/test/test-locale-util.c b/src/test/test-locale-util.c index c6f8c1fb4..28d90be89 100644 --- a/src/test/test-locale-util.c +++ b/src/test/test-locale-util.c @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include "kbd-util.h" #include "locale-util.h" #include "macro.h" #include "strv.h" +#include "util.h" static void test_get_locales(void) { _cleanup_strv_free_ char **locales = NULL; diff --git a/src/test/test-log.c b/src/test/test-log.c index 1e010c08f..18ef56ac3 100644 --- a/src/test/test-log.c +++ b/src/test/test-log.c @@ -59,15 +59,22 @@ static void test_long_lines(void) { "asdfasdf %s asdfasdfa", "foobar"); } +static void test_log_syntax(void) { + assert_se(log_syntax("unit", LOG_ERR, "filename", 10, EINVAL, "EINVAL: %s: %m", "hogehoge") == -EINVAL); + assert_se(log_syntax("unit", LOG_ERR, "filename", 10, -ENOENT, "ENOENT: %s: %m", "hogehoge") == -ENOENT); + assert_se(log_syntax("unit", LOG_ERR, "filename", 10, SYNTHETIC_ERRNO(ENOTTY), "ENOTTY: %s: %m", "hogehoge") == -ENOTTY); +} + int main(int argc, char* argv[]) { int target; - for (target = 0; target < _LOG_TARGET_MAX; target++) { + for (target = 0; target < _LOG_TARGET_MAX; target++) { log_set_target(target); log_open(); test_log_struct(); test_long_lines(); + test_log_syntax(); } assert_se(log_info_errno(SYNTHETIC_ERRNO(EUCLEAN), "foo") == -EUCLEAN); diff --git a/src/test/test-mountpoint-util.c b/src/test/test-mountpoint-util.c index 8e45c0b1a..8c5aac8eb 100644 --- a/src/test/test-mountpoint-util.c +++ b/src/test/test-mountpoint-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "def.h" @@ -36,7 +37,7 @@ static void test_mount_propagation_flags(const char *name, int ret, unsigned lon static void test_mnt_id(void) { _cleanup_fclose_ FILE *f = NULL; - Hashmap *h; + _cleanup_hashmap_free_free_ Hashmap *h = NULL; Iterator i; char *p; void *k; @@ -57,7 +58,14 @@ static void test_mnt_id(void) { assert_se(r > 0); assert_se(sscanf(line, "%i %*s %*s %*s %ms", &mnt_id, &path) == 2); - +#if HAS_FEATURE_MEMORY_SANITIZER + /* We don't know the length of the string, so we need to unpoison it one char at a time */ + for (const char *c = path; ;c++) { + msan_unpoison(c, 1); + if (!*c) + break; + } +#endif log_debug("mountinfo: %s → %i", path, mnt_id); assert_se(hashmap_put(h, INT_TO_PTR(mnt_id), path) >= 0); @@ -84,8 +92,6 @@ static void test_mnt_id(void) { 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) { diff --git a/src/test/test-namespace.c b/src/test/test-namespace.c index cc2efecfd..73ad2b25d 100644 --- a/src/test/test-namespace.c +++ b/src/test/test-namespace.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include #include "alloc-util.h" #include "fd-util.h" diff --git a/src/test/test-netlink-manual.c b/src/test/test-netlink-manual.c index 1ebe8d197..a1a5eb477 100644 --- a/src/test/test-netlink-manual.c +++ b/src/test/test-netlink-manual.c @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include "sd-netlink.h" diff --git a/src/test/test-nss.c b/src/test/test-nss.c index 20aa6cf01..27afe3608 100644 --- a/src/test/test-nss.c +++ b/src/test/test-nss.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "af-list.h" #include "alloc-util.h" @@ -12,11 +13,13 @@ #include "in-addr-util.h" #include "local-addresses.h" #include "log.h" +#include "main-func.h" #include "nss-util.h" #include "path-util.h" #include "stdio-util.h" #include "string-util.h" #include "strv.h" +#include "tests.h" static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) { switch (status) { @@ -485,7 +488,7 @@ static int parse_argv(int argc, char **argv, return 0; } -int main(int argc, char **argv) { +static int run(int argc, char **argv) { _cleanup_free_ char *dir = NULL; _cleanup_strv_free_ char **modules = NULL, **names = NULL; _cleanup_free_ struct local_address *addresses = NULL; @@ -493,8 +496,7 @@ int main(int argc, char **argv) { char **module; int r; - log_set_max_level(LOG_INFO); - log_parse_environment(); + test_setup_logging(LOG_INFO); r = parse_argv(argc, argv, &modules, &names, &addresses, &n_addresses); if (r < 0) { @@ -504,13 +506,15 @@ int main(int argc, char **argv) { dir = dirname_malloc(argv[0]); if (!dir) - return EXIT_FAILURE; + return log_oom(); STRV_FOREACH(module, modules) { r = test_one_module(dir, *module, names, addresses, n_addresses); if (r < 0) - return EXIT_FAILURE; + return r; } - return EXIT_SUCCESS; + return 0; } + +DEFINE_MAIN_FUNCTION(run); diff --git a/src/test/test-ordered-set.c b/src/test/test-ordered-set.c new file mode 100644 index 000000000..0d29fcfad --- /dev/null +++ b/src/test/test-ordered-set.c @@ -0,0 +1,121 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include + +#include "ordered-set.h" +#include "string-util.h" +#include "strv.h" + +static void test_set_steal_first(void) { + _cleanup_ordered_set_free_ OrderedSet *m = NULL; + int seen[3] = {}; + char *val; + + m = ordered_set_new(&string_hash_ops); + assert_se(m); + + assert_se(ordered_set_put(m, (void*) "1") == 1); + assert_se(ordered_set_put(m, (void*) "22") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 1); + + ordered_set_print(stdout, "SET=", m); + + while ((val = ordered_set_steal_first(m))) + seen[strlen(val) - 1]++; + + assert_se(seen[0] == 1 && seen[1] == 1 && seen[2] == 1); + + assert_se(ordered_set_isempty(m)); + + ordered_set_print(stdout, "SET=", m); +} + +typedef struct Item { + int seen; +} Item; +static void item_seen(Item *item) { + item->seen++; +} + +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) { + OrderedSet *m; + struct Item items[4] = {}; + unsigned i; + + assert_se(m = ordered_set_new(&item_hash_ops)); + for (i = 0; i < ELEMENTSOF(items) - 1; i++) + assert_se(ordered_set_put(m, items + i) == 1); + + m = ordered_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_ordered_set_free_ OrderedSet *m = NULL; + _cleanup_free_ char **t = NULL; + + m = ordered_set_new(&string_hash_ops); + assert_se(m); + + assert_se(ordered_set_put(m, (void*) "1") == 1); + assert_se(ordered_set_put(m, (void*) "22") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 0); + assert_se(ordered_set_remove(m, (void*) "333")); + assert_se(ordered_set_put(m, (void*) "333") == 1); + assert_se(ordered_set_put(m, (void*) "333") == 0); + assert_se(ordered_set_put(m, (void*) "22") == 0); + + assert_se(t = ordered_set_get_strv(m)); + assert_se(streq(t[0], "1")); + assert_se(streq(t[1], "22")); + assert_se(streq(t[2], "333")); + assert_se(!t[3]); + + ordered_set_print(stdout, "FOO=", m); +} + +static void test_set_put_string_set(void) { + _cleanup_ordered_set_free_free_ OrderedSet *m = NULL; + _cleanup_ordered_set_free_ OrderedSet *q = NULL; + _cleanup_free_ char **final = NULL; /* "just free" because the strings are in the set */ + void *t; + + m = ordered_set_new(&string_hash_ops); + assert_se(m); + + q = ordered_set_new(&string_hash_ops); + assert_se(q); + + assert_se(t = strdup("1")); + assert_se(ordered_set_put(m, t) == 1); + assert_se(t = strdup("22")); + assert_se(ordered_set_put(m, t) == 1); + assert_se(t = strdup("333")); + assert_se(ordered_set_put(m, t) == 1); + + assert_se(ordered_set_put(q, (void*) "11") == 1); + assert_se(ordered_set_put(q, (void*) "22") == 1); + assert_se(ordered_set_put(q, (void*) "33") == 1); + + assert_se(ordered_set_put_string_set(m, q) == 2); + + assert_se(final = ordered_set_get_strv(m)); + assert_se(strv_equal(final, STRV_MAKE("1", "22", "333", "11", "33"))); + + ordered_set_print(stdout, "BAR=", m); +} + +int main(int argc, const char *argv[]) { + test_set_steal_first(); + test_set_free_with_hash_ops(); + test_set_put(); + test_set_put_string_set(); + + return 0; +} diff --git a/src/test/test-path-util.c b/src/test/test-path-util.c index c64ca7b01..182991695 100644 --- a/src/test/test-path-util.c +++ b/src/test/test-path-util.c @@ -179,7 +179,14 @@ static void test_find_binary(const char *self) { } static void test_prefixes(void) { - static const char* values[] = { "/a/b/c/d", "/a/b/c", "/a/b", "/a", "", NULL}; + static const char* const values[] = { + "/a/b/c/d", + "/a/b/c", + "/a/b", + "/a", + "", + NULL + }; unsigned i; char s[PATH_MAX]; bool b; diff --git a/src/test/test-pretty-print.c b/src/test/test-pretty-print.c index 53ec512ec..3ad80d145 100644 --- a/src/test/test-pretty-print.c +++ b/src/test/test-pretty-print.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include +#include +#include #include "alloc-util.h" #include "macro.h" diff --git a/src/test/test-prioq.c b/src/test/test-prioq.c index 53c9e090a..21d5b44fb 100644 --- a/src/test/test-prioq.c +++ b/src/test/test-prioq.c @@ -6,7 +6,7 @@ #include "prioq.h" #include "set.h" #include "siphash24.h" -#include "util.h" +#include "sort-util.h" #define SET_SIZE 1024*4 diff --git a/src/test/test-proc-cmdline.c b/src/test/test-proc-cmdline.c index b0fc217d5..4e209de5b 100644 --- a/src/test/test-proc-cmdline.c +++ b/src/test/test-proc-cmdline.c @@ -92,7 +92,7 @@ static void test_proc_cmdline_get_key(void) { _cleanup_free_ char *value = NULL; log_info("/* %s */", __func__); - assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm spaaace='ö ü ß' ticks=\"''\"") == 0); + assert_se(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm spaaace='ö ü ß' ticks=\"''\"\n\nkkk=uuu\n\n\n") == 0); assert_se(proc_cmdline_get_key("", 0, &value) == -EINVAL); assert_se(proc_cmdline_get_key("abc", 0, NULL) == 0); @@ -129,13 +129,16 @@ static void test_proc_cmdline_get_key(void) { value = mfree(value); assert_se(proc_cmdline_get_key("ticks", 0, &value) > 0 && streq_ptr(value, "''")); + value = mfree(value); + + assert_se(proc_cmdline_get_key("kkk", 0, &value) > 0 && streq_ptr(value, "uuu")); } static void test_proc_cmdline_get_bool(void) { bool value = false; 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(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar bar-waldo=1 x_y-z=0 quux=miep\nda=yes\nthe=1") == 0); assert_se(proc_cmdline_get_bool("", &value) == -EINVAL); assert_se(proc_cmdline_get_bool("abc", &value) == 0 && value == false); @@ -148,13 +151,15 @@ static void test_proc_cmdline_get_bool(void) { assert_se(proc_cmdline_get_bool("x-y_z", &value) > 0 && value == false); assert_se(proc_cmdline_get_bool("x_y_z", &value) > 0 && value == false); assert_se(proc_cmdline_get_bool("quux", &value) == -EINVAL && value == false); + assert_se(proc_cmdline_get_bool("da", &value) > 0 && value == true); + assert_se(proc_cmdline_get_bool("the", &value) > 0 && value == true); } static void test_proc_cmdline_get_key_many(void) { - _cleanup_free_ char *value1 = NULL, *value2 = NULL, *value3 = NULL, *value4 = NULL, *value5 = NULL, *value6 = NULL; + _cleanup_free_ char *value1 = NULL, *value2 = NULL, *value3 = NULL, *value4 = NULL, *value5 = NULL, *value6 = NULL, *value7 = 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(putenv((char*) "SYSTEMD_PROC_CMDLINE=foo_bar=quux wuff-piep=tuet zumm SPACE='one two' doubleticks=\" aaa aaa \"\n\nzummm='\n'\n") == 0); assert_se(proc_cmdline_get_key_many(0, "wuff-piep", &value3, @@ -162,7 +167,9 @@ static void test_proc_cmdline_get_key_many(void) { "idontexist", &value2, "zumm", &value4, "SPACE", &value5, - "doubleticks", &value6) == 4); + "doubleticks", &value6, + "zummm", &value7) == 5); + assert_se(streq_ptr(value1, "quux")); assert_se(!value2); @@ -170,6 +177,7 @@ static void test_proc_cmdline_get_key_many(void) { assert_se(!value4); assert_se(streq_ptr(value5, "one two")); assert_se(streq_ptr(value6, " aaa aaa ")); + assert_se(streq_ptr(value7, "\n")); } static void test_proc_cmdline_key_streq(void) { diff --git a/src/test/test-process-util.c b/src/test/test-process-util.c index b5ba651d8..89f6618e2 100644 --- a/src/test/test-process-util.c +++ b/src/test/test-process-util.c @@ -603,8 +603,7 @@ static void test_ioprio_class_from_to_string(void) { int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); - saved_argc = argc; - saved_argv = argv; + save_argc_argv(argc, argv); if (argc > 1) { pid_t pid = 0; diff --git a/src/test/test-replace-var.c b/src/test/test-replace-var.c index 55208b179..8620e72b2 100644 --- a/src/test/test-replace-var.c +++ b/src/test/test-replace-var.c @@ -1,11 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ -#include +#include #include "macro.h" #include "replace-var.h" #include "string-util.h" -#include "util.h" static char *lookup(const char *variable, void *userdata) { return strjoin("<<<", variable, ">>>"); diff --git a/src/test/test-rlimit-util.c b/src/test/test-rlimit-util.c index 771719a26..fac30126c 100644 --- a/src/test/test-rlimit-util.c +++ b/src/test/test-rlimit-util.c @@ -8,7 +8,7 @@ #include "missing.h" #include "rlimit-util.h" #include "string-util.h" -#include "util.h" +#include "time-util.h" static void test_rlimit_parse_format(int resource, const char *string, rlim_t soft, rlim_t hard, int ret, const char *formatted) { _cleanup_free_ char *f = NULL; diff --git a/src/test/test-seccomp.c b/src/test/test-seccomp.c index fbfeedd53..9b7307cf3 100644 --- a/src/test/test-seccomp.c +++ b/src/test/test-seccomp.c @@ -7,21 +7,25 @@ #include #include #include +#include #include #include #include "alloc-util.h" #include "fd-util.h" #include "macro.h" +#include "memory-util.h" #include "missing.h" #include "nsflags.h" +#include "nulstr-util.h" #include "process-util.h" #include "raw-clone.h" +#include "rm-rf.h" #include "seccomp-util.h" #include "set.h" #include "string-util.h" #include "tests.h" -#include "util.h" +#include "tmpfile-util.h" #include "virt.h" #if SCMP_SYS(socket) < 0 || defined(__i386__) || defined(__s390x__) || defined(__s390__) @@ -506,9 +510,18 @@ static void test_memory_deny_write_execute_mmap(void) { static void test_memory_deny_write_execute_shmat(void) { int shmid; pid_t pid; + uint32_t arch; log_info("/* %s */", __func__); + SECCOMP_FOREACH_LOCAL_ARCH(arch) { + log_debug("arch %s: SCMP_SYS(mmap) = %d", seccomp_arch_to_string(arch), SCMP_SYS(mmap)); + log_debug("arch %s: SCMP_SYS(mmap2) = %d", seccomp_arch_to_string(arch), SCMP_SYS(mmap2)); + log_debug("arch %s: SCMP_SYS(shmget) = %d", seccomp_arch_to_string(arch), SCMP_SYS(shmget)); + log_debug("arch %s: SCMP_SYS(shmat) = %d", seccomp_arch_to_string(arch), SCMP_SYS(shmat)); + log_debug("arch %s: SCMP_SYS(shmdt) = %d", seccomp_arch_to_string(arch), SCMP_SYS(shmdt)); + } + if (!is_seccomp_available()) { log_notice("Seccomp not available, skipping %s", __func__); return; @@ -538,15 +551,18 @@ static void test_memory_deny_write_execute_shmat(void) { assert_se(seccomp_memory_deny_write_execute() >= 0); p = shmat(shmid, NULL, SHM_EXEC); + log_debug_errno(p == MAP_FAILED ? errno : 0, "shmat(SHM_EXEC): %m"); #if defined(__x86_64__) || defined(__arm__) || defined(__aarch64__) assert_se(p == MAP_FAILED); assert_se(errno == EPERM); -#else /* __i386__, __powerpc64__, and "unknown" architectures */ - assert_se(p != MAP_FAILED); - assert_se(shmdt(p) == 0); #endif + /* Depending on kernel, libseccomp, and glibc versions, other architectures + * might fail or not. Let's not assert success. */ + if (p != MAP_FAILED) + assert_se(shmdt(p) == 0); p = shmat(shmid, NULL, 0); + log_debug_errno(p == MAP_FAILED ? errno : 0, "shmat(0): %m"); assert_se(p != MAP_FAILED); assert_se(shmdt(p) == 0); @@ -746,6 +762,216 @@ static void test_lock_personality(void) { assert_se(wait_for_terminate_and_check("lockpersonalityseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); } +static int real_open(const char *path, int flags, mode_t mode) { + /* glibc internally calls openat() when open() is requested. Let's hence define our own wrapper for + * testing purposes that calls the real syscall, on architectures where SYS_open is defined. On + * other architectures, let's just fall back to the glibc call. */ + +#ifdef SYS_open + return (int) syscall(SYS_open, path, flags, mode); +#else + return open(path, flags, mode); +#endif +} + +static void test_restrict_suid_sgid(void) { + pid_t pid; + + log_info("/* %s */", __func__); + + if (!is_seccomp_available()) { + log_notice("Seccomp not available, skipping %s", __func__); + return; + } + if (geteuid() != 0) { + log_notice("Not root, skipping %s", __func__); + return; + } + + pid = fork(); + assert_se(pid >= 0); + + if (pid == 0) { + char path[] = "/tmp/suidsgidXXXXXX", dir[] = "/tmp/suidsgiddirXXXXXX"; + int fd = -1, k = -1; + const char *z; + + fd = mkostemp_safe(path); + assert_se(fd >= 0); + + assert_se(mkdtemp(dir)); + z = strjoina(dir, "/test"); + + assert_se(chmod(path, 0755 | S_ISUID) >= 0); + assert_se(chmod(path, 0755 | S_ISGID) >= 0); + assert_se(chmod(path, 0755 | S_ISGID | S_ISUID) >= 0); + assert_se(chmod(path, 0755) >= 0); + + assert_se(fchmod(fd, 0755 | S_ISUID) >= 0); + assert_se(fchmod(fd, 0755 | S_ISGID) >= 0); + assert_se(fchmod(fd, 0755 | S_ISGID | S_ISUID) >= 0); + assert_se(fchmod(fd, 0755) >= 0); + + assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISUID, 0) >= 0); + assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID, 0) >= 0); + assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) >= 0); + assert_se(fchmodat(AT_FDCWD, path, 0755, 0) >= 0); + + k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = creat(z, 0644 | S_ISUID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = creat(z, 0644 | S_ISGID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = creat(z, 0644 | S_ISUID | S_ISGID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = creat(z, 0644); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + assert_se(mkdir(z, 0755 | S_ISUID) >= 0); + assert_se(rmdir(z) >= 0); + assert_se(mkdir(z, 0755 | S_ISGID) >= 0); + assert_se(rmdir(z) >= 0); + assert_se(mkdir(z, 0755 | S_ISUID | S_ISGID) >= 0); + assert_se(rmdir(z) >= 0); + assert_se(mkdir(z, 0755) >= 0); + assert_se(rmdir(z) >= 0); + + assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID) >= 0); + assert_se(rmdir(z) >= 0); + assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISGID) >= 0); + assert_se(rmdir(z) >= 0); + assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID | S_ISGID) >= 0); + assert_se(rmdir(z) >= 0); + assert_se(mkdirat(AT_FDCWD, z, 0755) >= 0); + assert_se(rmdir(z) >= 0); + + assert_se(mknod(z, S_IFREG | 0755 | S_ISUID, 0) >= 0); + assert_se(unlink(z) >= 0); + assert_se(mknod(z, S_IFREG | 0755 | S_ISGID, 0) >= 0); + assert_se(unlink(z) >= 0); + assert_se(mknod(z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) >= 0); + assert_se(unlink(z) >= 0); + assert_se(mknod(z, S_IFREG | 0755, 0) >= 0); + assert_se(unlink(z) >= 0); + + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID, 0) >= 0); + assert_se(unlink(z) >= 0); + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISGID, 0) >= 0); + assert_se(unlink(z) >= 0); + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) >= 0); + assert_se(unlink(z) >= 0); + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755, 0) >= 0); + assert_se(unlink(z) >= 0); + + assert_se(seccomp_restrict_suid_sgid() >= 0); + + assert_se(chmod(path, 0775 | S_ISUID) < 0 && errno == EPERM); + assert_se(chmod(path, 0775 | S_ISGID) < 0 && errno == EPERM); + assert_se(chmod(path, 0775 | S_ISGID | S_ISUID) < 0 && errno == EPERM); + assert_se(chmod(path, 0775) >= 0); + + assert_se(fchmod(fd, 0775 | S_ISUID) < 0 && errno == EPERM); + assert_se(fchmod(fd, 0775 | S_ISGID) < 0 && errno == EPERM); + assert_se(fchmod(fd, 0775 | S_ISGID | S_ISUID) < 0 && errno == EPERM); + assert_se(fchmod(fd, 0775) >= 0); + + assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISUID, 0) < 0 && errno == EPERM); + assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID, 0) < 0 && errno == EPERM); + assert_se(fchmodat(AT_FDCWD, path, 0755 | S_ISGID | S_ISUID, 0) < 0 && errno == EPERM); + assert_se(fchmodat(AT_FDCWD, path, 0755, 0) >= 0); + + assert_se(real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID) < 0 && errno == EPERM); + assert_se(real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID) < 0 && errno == EPERM); + assert_se(real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID) < 0 && errno == EPERM); + k = real_open(z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + assert_se(creat(z, 0644 | S_ISUID) < 0 && errno == EPERM); + assert_se(creat(z, 0644 | S_ISGID) < 0 && errno == EPERM); + assert_se(creat(z, 0644 | S_ISUID | S_ISGID) < 0 && errno == EPERM); + k = creat(z, 0644); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + assert_se(openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID) < 0 && errno == EPERM); + assert_se(openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISGID) < 0 && errno == EPERM); + assert_se(openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644 | S_ISUID | S_ISGID) < 0 && errno == EPERM); + k = openat(AT_FDCWD, z, O_CREAT|O_RDWR|O_CLOEXEC|O_EXCL, 0644); + k = safe_close(k); + assert_se(unlink(z) >= 0); + + assert_se(mkdir(z, 0755 | S_ISUID) < 0 && errno == EPERM); + assert_se(mkdir(z, 0755 | S_ISGID) < 0 && errno == EPERM); + assert_se(mkdir(z, 0755 | S_ISUID | S_ISGID) < 0 && errno == EPERM); + assert_se(mkdir(z, 0755) >= 0); + assert_se(rmdir(z) >= 0); + + assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID) < 0 && errno == EPERM); + assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISGID) < 0 && errno == EPERM); + assert_se(mkdirat(AT_FDCWD, z, 0755 | S_ISUID | S_ISGID) < 0 && errno == EPERM); + assert_se(mkdirat(AT_FDCWD, z, 0755) >= 0); + assert_se(rmdir(z) >= 0); + + assert_se(mknod(z, S_IFREG | 0755 | S_ISUID, 0) < 0 && errno == EPERM); + assert_se(mknod(z, S_IFREG | 0755 | S_ISGID, 0) < 0 && errno == EPERM); + assert_se(mknod(z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) < 0 && errno == EPERM); + assert_se(mknod(z, S_IFREG | 0755, 0) >= 0); + assert_se(unlink(z) >= 0); + + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID, 0) < 0 && errno == EPERM); + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISGID, 0) < 0 && errno == EPERM); + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755 | S_ISUID | S_ISGID, 0) < 0 && errno == EPERM); + assert_se(mknodat(AT_FDCWD, z, S_IFREG | 0755, 0) >= 0); + assert_se(unlink(z) >= 0); + + assert_se(unlink(path) >= 0); + assert_se(rm_rf(dir, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0); + + _exit(EXIT_SUCCESS); + } + + assert_se(wait_for_terminate_and_check("suidsgidseccomp", pid, WAIT_LOG) == EXIT_SUCCESS); +} + int main(int argc, char *argv[]) { test_setup_logging(LOG_DEBUG); @@ -763,6 +989,7 @@ int main(int argc, char *argv[]) { test_restrict_archs(); test_load_syscall_filter_set_raw(); test_lock_personality(); + test_restrict_suid_sgid(); return 0; } diff --git a/src/test/test-set.c b/src/test/test-set.c index 340edeb65..b4e7a52fd 100644 --- a/src/test/test-set.c +++ b/src/test/test-set.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "set.h" +#include "strv.h" static void test_set_steal_first(void) { _cleanup_set_free_ Set *m = NULL; @@ -77,6 +78,12 @@ static void test_set_put(void) { assert_se(set_put(m, (void*) "333") == 1); assert_se(set_put(m, (void*) "333") == 0); assert_se(set_put(m, (void*) "22") == 0); + + _cleanup_free_ char **t = set_get_strv(m); + assert_se(strv_contains(t, "1")); + assert_se(strv_contains(t, "22")); + assert_se(strv_contains(t, "333")); + assert_se(strv_length(t) == 3); } int main(int argc, const char *argv[]) { diff --git a/src/test/test-sigbus.c b/src/test/test-sigbus.c index d2666dd1d..f753e3836 100644 --- a/src/test/test-sigbus.c +++ b/src/test/test-sigbus.c @@ -1,15 +1,17 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include +#include #if HAVE_VALGRIND_VALGRIND_H # include #endif #include "fd-util.h" +#include "memory-util.h" #include "sigbus.h" #include "tests.h" -#include "util.h" int main(int argc, char *argv[]) { _cleanup_close_ int fd = -1; diff --git a/src/test/test-siphash24.c b/src/test/test-siphash24.c index 218200e48..6ae272fa9 100644 --- a/src/test/test-siphash24.c +++ b/src/test/test-siphash24.c @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include "memory-util.h" #include "siphash24.h" -#include "util.h" #define ITERATIONS 10000000ULL diff --git a/src/test/test-sleep.c b/src/test/test-sleep.c index 2a6d5e765..e0c513ad8 100644 --- a/src/test/test-sleep.c +++ b/src/test/test-sleep.c @@ -1,11 +1,15 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include #include +#include +#include #include "fd-util.h" #include "log.h" +#include "memory-util.h" #include "sleep-config.h" #include "strv.h" #include "tests.h" diff --git a/src/test/test-socket-util.c b/src/test/test-socket-util.c index 07ff48783..057840d76 100644 --- a/src/test/test-socket-util.c +++ b/src/test/test-socket-util.c @@ -1,8 +1,10 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include #include #include -#include #include "alloc-util.h" #include "async.h" @@ -19,7 +21,6 @@ #include "string-util.h" #include "tests.h" #include "tmpfile-util.h" -#include "util.h" static void test_ifname_valid(void) { log_info("/* %s */", __func__); @@ -164,20 +165,35 @@ static void test_socket_address_parse_netlink(void) { assert_se(socket_address_parse_netlink(&a, "junk") < 0); assert_se(socket_address_parse_netlink(&a, "") < 0); + assert_se(socket_address_parse_netlink(&a, "route") >= 0); + assert_se(a.sockaddr.nl.nl_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_groups == 0); + assert_se(a.protocol == NETLINK_ROUTE); assert_se(socket_address_parse_netlink(&a, "route") >= 0); assert_se(socket_address_parse_netlink(&a, "route 10") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_groups == 10); assert_se(a.protocol == NETLINK_ROUTE); /* With spaces and tabs */ assert_se(socket_address_parse_netlink(&a, " kobject-uevent ") >= 0); - assert_se(socket_address_parse_netlink(&a, " \t kobject-uevent \t 10 \t") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_groups == 0); + assert_se(a.protocol == NETLINK_KOBJECT_UEVENT); + assert_se(socket_address_parse_netlink(&a, " \t kobject-uevent \t 10") >= 0); + assert_se(a.sockaddr.nl.nl_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_groups == 10); + assert_se(a.protocol == NETLINK_KOBJECT_UEVENT); + assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10") >= 0); + assert_se(a.sockaddr.nl.nl_family == AF_NETLINK); + assert_se(a.sockaddr.nl.nl_groups == 10); assert_se(a.protocol == NETLINK_KOBJECT_UEVENT); - assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10") >= 0); - assert_se(a.sockaddr.sa.sa_family == AF_NETLINK); - assert_se(a.protocol == NETLINK_KOBJECT_UEVENT); + /* trailing space is not supported */ + assert_se(socket_address_parse_netlink(&a, "kobject-uevent\t10 ") < 0); + + /* Group must be unsigned */ + assert_se(socket_address_parse_netlink(&a, "kobject-uevent -1") < 0); /* oss-fuzz #6884 */ assert_se(socket_address_parse_netlink(&a, "\xff") < 0); diff --git a/src/test/test-stat-util.c b/src/test/test-stat-util.c index 0e2155e91..e3d89d6e2 100644 --- a/src/test/test-stat-util.c +++ b/src/test/test-stat-util.c @@ -9,6 +9,7 @@ #include "macro.h" #include "missing.h" #include "mountpoint-util.h" +#include "namespace-util.h" #include "path-util.h" #include "stat-util.h" #include "tmpfile-util.h" diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c index 8c1f91d4e..064a5abda 100644 --- a/src/test/test-string-util.c +++ b/src/test/test-string-util.c @@ -7,6 +7,7 @@ #include "strv.h" #include "tests.h" #include "utf8.h" +#include "util.h" static void test_string_erase(void) { char *x; diff --git a/src/test/test-strv.c b/src/test/test-strv.c index 31ef1abb4..18ad03669 100644 --- a/src/test/test-strv.c +++ b/src/test/test-strv.c @@ -4,10 +4,10 @@ #include "alloc-util.h" #include "escape.h" +#include "nulstr-util.h" #include "specifier.h" #include "string-util.h" #include "strv.h" -#include "util.h" static void test_specifier_printf(void) { static const Specifier table[] = { diff --git a/src/test/test-tables.c b/src/test/test-tables.c index 49268eae2..59f90b76e 100644 --- a/src/test/test-tables.c +++ b/src/test/test-tables.c @@ -5,7 +5,7 @@ #include "cgroup.h" #include "compress.h" #include "condition.h" -#include "device-internal.h" +#include "device-private.h" #include "device.h" #include "execute.h" #include "import-util.h" @@ -70,7 +70,7 @@ int main(int argc, char **argv) { test_table(kill_who, KILL_WHO); test_table(locale_variable, VARIABLE_LC); test_table(log_target, LOG_TARGET); - test_table(mac_policy, MACPOLICY); + test_table(mac_address_policy, MAC_ADDRESS_POLICY); test_table(manager_state, MANAGER_STATE); test_table(manager_timestamp, MANAGER_TIMESTAMP); test_table(mount_exec_command, MOUNT_EXEC_COMMAND); diff --git a/src/test/test-terminal-util.c b/src/test/test-terminal-util.c index 958d36943..96d92279f 100644 --- a/src/test/test-terminal-util.c +++ b/src/test/test-terminal-util.c @@ -1,11 +1,14 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include #include #include +#include #include "alloc-util.h" #include "fd-util.h" #include "macro.h" +#include "path-util.h" #include "strv.h" #include "terminal-util.h" #include "tests.h" @@ -13,6 +16,8 @@ #include "util.h" static void test_default_term_for_tty(void) { + log_info("/* %s */", __func__); + puts(default_term_for_tty("/dev/tty23")); puts(default_term_for_tty("/dev/ttyS23")); puts(default_term_for_tty("/dev/tty0")); @@ -33,7 +38,9 @@ static void test_read_one_char(void) { bool need_nl; char name[] = "/tmp/test-read_one_char.XXXXXX"; - assert(fmkostemp_safe(name, "r+", &file) == 0); + log_info("/* %s */", __func__); + + assert_se(fmkostemp_safe(name, "r+", &file) == 0); assert_se(fputs("c\n", file) >= 0); rewind(file); @@ -52,7 +59,20 @@ static void test_read_one_char(void) { rewind(file); assert_se(read_one_char(file, &r, 1000000, &need_nl) < 0); - unlink(name); + assert_se(unlink(name) >= 0); +} + +static void test_getttyname_malloc(void) { + _cleanup_free_ char *ttyname = NULL; + _cleanup_close_ int master = -1; + + log_info("/* %s */", __func__); + + assert_se((master = posix_openpt(O_RDWR|O_NOCTTY)) >= 0); + assert_se(getttyname_malloc(master, &ttyname) >= 0); + log_info("ttyname = %s", ttyname); + + assert_se(PATH_IN_SET(ttyname, "ptmx", "pts/ptmx")); } int main(int argc, char *argv[]) { @@ -60,6 +80,7 @@ int main(int argc, char *argv[]) { test_default_term_for_tty(); test_read_one_char(); + test_getttyname_malloc(); return 0; } diff --git a/src/test/test-time-util.c b/src/test/test-time-util.c index eb6041c15..d05bb6142 100644 --- a/src/test/test-time-util.c +++ b/src/test/test-time-util.c @@ -4,6 +4,7 @@ #include "serialize.h" #include "string-util.h" #include "strv.h" +#include "tests.h" #include "time-util.h" static void test_parse_sec(void) { @@ -85,6 +86,26 @@ static void test_parse_sec_fix_0(void) { assert_se(u == USEC_INFINITY); } +static void test_parse_sec_def_infinity(void) { + usec_t u; + + log_info("/* %s */", __func__); + + assert_se(parse_sec_def_infinity("5s", &u) >= 0); + assert_se(u == 5 * USEC_PER_SEC); + assert_se(parse_sec_def_infinity("", &u) >= 0); + assert_se(u == USEC_INFINITY); + assert_se(parse_sec_def_infinity(" ", &u) >= 0); + assert_se(u == USEC_INFINITY); + assert_se(parse_sec_def_infinity("0s", &u) >= 0); + assert_se(u == 0); + assert_se(parse_sec_def_infinity("0", &u) >= 0); + assert_se(u == 0); + assert_se(parse_sec_def_infinity(" 0", &u) >= 0); + assert_se(u == 0); + assert_se(parse_sec_def_infinity("-5s", &u) < 0); +} + static void test_parse_time(void) { usec_t u; @@ -240,8 +261,10 @@ static void test_get_timezones(void) { r = get_timezones(&zones); assert_se(r == 0); - STRV_FOREACH(zone, zones) + STRV_FOREACH(zone, zones) { + log_info("zone: %s", *zone); assert_se(timezone_is_valid(*zone, LOG_ERR)); + } } static void test_usec_add(void) { @@ -461,7 +484,7 @@ static void test_in_utc_timezone(void) { } int main(int argc, char *argv[]) { - uintmax_t x; + test_setup_logging(LOG_INFO); log_info("realtime=" USEC_FMT "\n" "monotonic=" USEC_FMT "\n" @@ -472,6 +495,7 @@ int main(int argc, char *argv[]) { test_parse_sec(); test_parse_sec_fix_0(); + test_parse_sec_def_infinity(); test_parse_time(); test_parse_nsec(); test_format_timespan(1); @@ -492,7 +516,7 @@ int main(int argc, char *argv[]) { assert_cc((time_t) -1 < (time_t) 1); /* Ensure TIME_T_MAX works correctly */ - x = (uintmax_t) TIME_T_MAX; + uintmax_t x = TIME_T_MAX; x++; assert((time_t) x < 0); diff --git a/src/test/test-unaligned.c b/src/test/test-unaligned.c index 96d6b22be..3d6f9a03b 100644 --- a/src/test/test-unaligned.c +++ b/src/test/test-unaligned.c @@ -1,8 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include "memory-util.h" #include "sparse-endian.h" #include "unaligned.h" -#include "util.h" static uint8_t data[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, diff --git a/src/test/test-unit-file.c b/src/test/test-unit-file.c index f5578f9fc..8a0251e13 100644 --- a/src/test/test-unit-file.c +++ b/src/test/test-unit-file.c @@ -13,6 +13,7 @@ #include "conf-parser.h" #include "env-file.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "hashmap.h" #include "hostname-util.h" @@ -20,6 +21,7 @@ #include "install.h" #include "load-fragment.h" #include "macro.h" +#include "memory-util.h" #include "rm-rf.h" #include "specifier.h" #include "string-util.h" @@ -28,7 +30,6 @@ #include "tests.h" #include "tmpfile-util.h" #include "user-util.h" -#include "util.h" static int test_unit_file_get_set(void) { int r; diff --git a/src/test/test-unit-name.c b/src/test/test-unit-name.c index 0d3674cff..fd2443e3c 100644 --- a/src/test/test-unit-name.c +++ b/src/test/test-unit-name.c @@ -8,6 +8,7 @@ #include "alloc-util.h" #include "all-units.h" #include "glob-util.h" +#include "format-util.h" #include "hostname-util.h" #include "macro.h" #include "manager.h" @@ -759,7 +760,7 @@ static void test_unit_name_from_dbus_path(void) { test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dcoredump_2esocket", 0, "systemd-coredump.socket"); test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dcoredump_400_2eservice", 0, "systemd-coredump@0.service"); test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dfirstboot_2eservice", 0, "systemd-firstboot.service"); - test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dfsck_2droot_2eservice", 0, "systemd-fsck-root.service"); + test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dfsck_2droot_2eservice", 0, SPECIAL_FSCK_ROOT_SERVICE); test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dhwdb_2dupdate_2eservice", 0, "systemd-hwdb-update.service"); test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dinitctl_2eservice", 0, "systemd-initctl.service"); test_unit_name_from_dbus_path_one("/org/freedesktop/systemd1/unit/systemd_2dinitctl_2esocket", 0, "systemd-initctl.socket"); diff --git a/src/test/test-user-util.c b/src/test/test-user-util.c index 801824ad6..1a507bc00 100644 --- a/src/test/test-user-util.c +++ b/src/test/test-user-util.c @@ -1,12 +1,12 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include "alloc-util.h" +#include "format-util.h" #include "log.h" #include "macro.h" +#include "path-util.h" #include "string-util.h" #include "user-util.h" -#include "util.h" -#include "path-util.h" static void test_uid_to_name_one(uid_t uid, const char *name) { _cleanup_free_ char *t = NULL; diff --git a/src/test/test-utf8.c b/src/test/test-utf8.c index 9849530ac..d1e48da2a 100644 --- a/src/test/test-utf8.c +++ b/src/test/test-utf8.c @@ -36,11 +36,21 @@ static void test_ascii_is_valid_n(void) { } static void test_utf8_encoded_valid_unichar(void) { - assert_se(utf8_encoded_valid_unichar("\342\204\242") == 3); - assert_se(utf8_encoded_valid_unichar("\302\256") == 2); - assert_se(utf8_encoded_valid_unichar("a") == 1); - assert_se(utf8_encoded_valid_unichar("\341\204") < 0); - assert_se(utf8_encoded_valid_unichar("\341\204\341\204") < 0); + assert_se(utf8_encoded_valid_unichar("\342\204\242", 1) == -EINVAL); /* truncated */ + assert_se(utf8_encoded_valid_unichar("\342\204\242", 2) == -EINVAL); /* truncated */ + assert_se(utf8_encoded_valid_unichar("\342\204\242", 3) == 3); + assert_se(utf8_encoded_valid_unichar("\342\204\242", 4) == 3); + assert_se(utf8_encoded_valid_unichar("\302\256", 1) == -EINVAL); /* truncated */ + assert_se(utf8_encoded_valid_unichar("\302\256", 2) == 2); + assert_se(utf8_encoded_valid_unichar("\302\256", 3) == 2); + assert_se(utf8_encoded_valid_unichar("\302\256", (size_t) -1) == 2); + assert_se(utf8_encoded_valid_unichar("a", 1) == 1); + assert_se(utf8_encoded_valid_unichar("a", 2) == 1); + assert_se(utf8_encoded_valid_unichar("\341\204", 1) == -EINVAL); /* truncated, potentially valid */ + assert_se(utf8_encoded_valid_unichar("\341\204", 2) == -EINVAL); /* truncated, potentially valid */ + assert_se(utf8_encoded_valid_unichar("\341\204", 3) == -EINVAL); + assert_se(utf8_encoded_valid_unichar("\341\204\341\204", 4) == -EINVAL); + assert_se(utf8_encoded_valid_unichar("\341\204\341\204", 5) == -EINVAL); } static void test_utf8_escaping(void) { diff --git a/src/test/test-util.c b/src/test/test-util.c index ffacd6566..120baa359 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -5,9 +5,10 @@ #include #include -#include "def.h" #include "fileio.h" #include "fs-util.h" +#include "limits-util.h" +#include "memory-util.h" #include "missing_syscall.h" #include "parse-util.h" #include "process-util.h" diff --git a/src/test/test-verbs.c b/src/test/test-verbs.c index efb9664b3..24593c75d 100644 --- a/src/test/test-verbs.c +++ b/src/test/test-verbs.c @@ -1,5 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include + #include "macro.h" #include "strv.h" #include "verbs.h" diff --git a/src/test/test-watch-pid.c b/src/test/test-watch-pid.c index 2c6ca0a1a..04d7e1fbd 100644 --- a/src/test/test-watch-pid.c +++ b/src/test/test-watch-pid.c @@ -42,25 +42,25 @@ int main(int argc, char *argv[]) { assert_se(hashmap_isempty(m->watch_pids)); assert_se(manager_get_unit_by_pid(m, 4711) == NULL); - assert_se(unit_watch_pid(a, 4711) >= 0); + assert_se(unit_watch_pid(a, 4711, false) >= 0); assert_se(manager_get_unit_by_pid(m, 4711) == a); - assert_se(unit_watch_pid(a, 4711) >= 0); + assert_se(unit_watch_pid(a, 4711, false) >= 0); assert_se(manager_get_unit_by_pid(m, 4711) == a); - assert_se(unit_watch_pid(b, 4711) >= 0); + assert_se(unit_watch_pid(b, 4711, false) >= 0); u = manager_get_unit_by_pid(m, 4711); assert_se(u == a || u == b); - assert_se(unit_watch_pid(b, 4711) >= 0); + assert_se(unit_watch_pid(b, 4711, false) >= 0); u = manager_get_unit_by_pid(m, 4711); assert_se(u == a || u == b); - assert_se(unit_watch_pid(c, 4711) >= 0); + assert_se(unit_watch_pid(c, 4711, false) >= 0); u = manager_get_unit_by_pid(m, 4711); assert_se(u == a || u == b || u == c); - assert_se(unit_watch_pid(c, 4711) >= 0); + assert_se(unit_watch_pid(c, 4711, false) >= 0); u = manager_get_unit_by_pid(m, 4711); assert_se(u == a || u == b || u == c); diff --git a/src/timedate/timedatectl.c b/src/timedate/timedatectl.c index 1e7b26276..406ca1df6 100644 --- a/src/timedate/timedatectl.c +++ b/src/timedate/timedatectl.c @@ -670,7 +670,7 @@ static int print_timesync_property(const char *name, const char *expected_value, return r; if (arg_all || !isempty(str)) - bus_print_property_value(name, expected_value, value, "%s", str); + bus_print_property_value(name, expected_value, value, str); return 1; } diff --git a/src/timedate/timedated.c b/src/timedate/timedated.c index eeb17b613..fe91854ac 100644 --- a/src/timedate/timedated.c +++ b/src/timedate/timedated.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "sd-bus.h" @@ -20,6 +22,7 @@ #include "hashmap.h" #include "list.h" #include "main-func.h" +#include "memory-util.h" #include "missing_capability.h" #include "path-util.h" #include "selinux-util.h" @@ -29,7 +32,6 @@ #include "unit-def.h" #include "unit-name.h" #include "user-util.h" -#include "util.h" #define NULL_ADJTIME_UTC "0.0 0 0\n0\nUTC\n" #define NULL_ADJTIME_LOCAL "0.0 0 0\n0\nLOCAL\n" @@ -377,9 +379,9 @@ static int match_job_removed(sd_bus_message *m, void *userdata, sd_bus_error *er 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); + + (void) sd_bus_emit_properties_changed(sd_bus_message_get_bus(m), "/org/freedesktop/timedate1", "org.freedesktop.timedate1", "NTP", NULL); } return 0; diff --git a/src/timesync/timesyncd-manager.c b/src/timesync/timesyncd-manager.c index 6fde4a316..4c00fa409 100644 --- a/src/timesync/timesyncd-manager.c +++ b/src/timesync/timesyncd-manager.c @@ -17,6 +17,7 @@ #include "alloc-util.h" #include "dns-domain.h" #include "fd-util.h" +#include "format-util.h" #include "fs-util.h" #include "list.h" #include "log.h" diff --git a/src/timesync/timesyncd.c b/src/timesync/timesyncd.c index b4f70c000..73d40c094 100644 --- a/src/timesync/timesyncd.c +++ b/src/timesync/timesyncd.c @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include + #include "sd-daemon.h" #include "sd-event.h" diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c index b66765b40..d9d1cc1c1 100644 --- a/src/tmpfiles/tmpfiles.c +++ b/src/tmpfiles/tmpfiles.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -48,9 +49,11 @@ #include "path-lookup.h" #include "path-util.h" #include "pretty-print.h" +#include "rlimit-util.h" #include "rm-rf.h" #include "selinux-util.h" #include "set.h" +#include "sort-util.h" #include "specifier.h" #include "stat-util.h" #include "stdio-util.h" @@ -59,7 +62,6 @@ #include "strv.h" #include "umask-util.h" #include "user-util.h" -#include "util.h" /* This reads all files listed in /etc/tmpfiles.d/?*.conf and creates * them in the file system. This is intended to be used to create @@ -525,7 +527,6 @@ static int dir_cleanup( bool keep_this_level) { struct dirent *dent; - struct timespec times[2]; bool deleted = false; int r = 0; @@ -580,42 +581,45 @@ static int dir_cleanup( } if (S_ISDIR(s.st_mode)) { + _cleanup_closedir_ DIR *sub_dir = NULL; if (mountpoint && streq(dent->d_name, "lost+found") && s.st_uid == 0) { - log_debug("Ignoring \"%s\".", sub_path); + log_debug("Ignoring directory \"%s\".", sub_path); continue; } if (maxdepth <= 0) log_warning("Reached max depth on \"%s\".", sub_path); else { - _cleanup_closedir_ DIR *sub_dir; int q; sub_dir = xopendirat_nomod(dirfd(d), dent->d_name); if (!sub_dir) { if (errno != ENOENT) - r = log_error_errno(errno, "opendir(%s) failed: %m", sub_path); + r = log_warning_errno(errno, "Opening directory \"%s\" failed, ignoring: %m", sub_path); continue; } + if (flock(dirfd(sub_dir), LOCK_EX|LOCK_NB) < 0) { + log_debug_errno(errno, "Couldn't acquire shared BSD lock on directory \"%s\", skipping: %m", p); + continue; + } + q = dir_cleanup(i, sub_path, sub_dir, &s, cutoff, rootdev, false, maxdepth-1, false); if (q < 0) r = q; } - /* Note: if you are wondering why we don't - * support the sticky bit for excluding - * directories from cleaning like we do it for - * other file system objects: well, the sticky - * bit already has a meaning for directories, - * so we don't want to overload that. */ + /* Note: if you are wondering why we don't support the sticky bit for excluding + * directories from cleaning like we do it for other file system objects: well, the + * sticky bit already has a meaning for directories, so we don't want to overload + * that. */ if (keep_this_level) { - log_debug("Keeping \"%s\".", sub_path); + log_debug("Keeping directory \"%s\".", sub_path); continue; } @@ -642,13 +646,11 @@ static int dir_cleanup( log_debug("Removing directory \"%s\".", sub_path); if (unlinkat(dirfd(d), dent->d_name, AT_REMOVEDIR) < 0) if (!IN_SET(errno, ENOENT, ENOTEMPTY)) - r = log_error_errno(errno, "rmdir(%s): %m", sub_path); + r = log_warning_errno(errno, "Failed to remove directory \"%s\", ignoring: %m", sub_path); } else { - /* Skip files for which the sticky bit is - * set. These are semantics we define, and are - * unknown elsewhere. See XDG_RUNTIME_DIR - * specification for details. */ + /* Skip files for which the sticky bit is set. These are semantics we define, and are + * unknown elsewhere. See XDG_RUNTIME_DIR specification for details. */ if (s.st_mode & S_ISVTX) { log_debug("Skipping \"%s\": sticky bit set.", sub_path); continue; @@ -675,8 +677,7 @@ static int dir_cleanup( continue; } - /* Keep files on this level around if this is - * requested */ + /* Keep files on this level around if this is requested */ if (keep_this_level) { log_debug("Keeping \"%s\".", sub_path); continue; @@ -710,11 +711,10 @@ static int dir_cleanup( continue; } - log_debug("unlink \"%s\"", sub_path); - + log_debug("Removing \"%s\".", sub_path); if (unlinkat(dirfd(d), dent->d_name, 0) < 0) if (errno != ENOENT) - r = log_error_errno(errno, "unlink(%s): %m", sub_path); + r = log_warning_errno(errno, "Failed to remove \"%s\", ignoring: %m", sub_path); deleted = true; } @@ -722,21 +722,22 @@ static int dir_cleanup( finish: if (deleted) { - usec_t age1, age2; char a[FORMAT_TIMESTAMP_MAX], b[FORMAT_TIMESTAMP_MAX]; - - /* Restore original directory timestamps */ - times[0] = ds->st_atim; - times[1] = ds->st_mtim; + usec_t age1, age2; age1 = timespec_load(&ds->st_atim); age2 = timespec_load(&ds->st_mtim); + log_debug("Restoring access and modification time on \"%s\": %s, %s", p, format_timestamp_us(a, sizeof(a), age1), format_timestamp_us(b, sizeof(b), age2)); - if (futimens(dirfd(d), times) < 0) - log_error_errno(errno, "utimensat(%s): %m", p); + + /* Restore original directory timestamps */ + if (futimens(dirfd(d), (struct timespec[]) { + ds->st_atim, + ds->st_mtim }) < 0) + log_warning_errno(errno, "Failed to revert timestamps of '%s', ignoring: %m", p); } return r; @@ -855,7 +856,7 @@ static int path_open_parent_safe(const char *path) { if (!dn) return log_oom(); - fd = chase_symlinks(dn, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_WARN, NULL); + fd = chase_symlinks(dn, arg_root, CHASE_OPEN|CHASE_SAFE|CHASE_WARN, NULL); if (fd < 0 && fd != -ENOLINK) return log_error_errno(fd, "Failed to validate path %s: %m", path); @@ -876,7 +877,7 @@ static int path_open_safe(const char *path) { "Failed to open invalid path '%s'.", path); - fd = chase_symlinks(path, NULL, CHASE_OPEN|CHASE_SAFE|CHASE_WARN|CHASE_NOFOLLOW, NULL); + fd = chase_symlinks(path, arg_root, 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); @@ -1095,22 +1096,6 @@ static int path_set_acls(Item *item, const char *path) { return r; } -#define ATTRIBUTES_ALL \ - (FS_NOATIME_FL | \ - FS_SYNC_FL | \ - FS_DIRSYNC_FL | \ - FS_APPEND_FL | \ - FS_COMPR_FL | \ - FS_NODUMP_FL | \ - FS_EXTENT_FL | \ - FS_IMMUTABLE_FL | \ - FS_JOURNAL_DATA_FL | \ - FS_SECRM_FL | \ - FS_UNRM_FL | \ - FS_NOTAIL_FL | \ - FS_TOPDIR_FL | \ - FS_NOCOW_FL) - static int parse_attribute_from_arg(Item *item) { static const struct { @@ -1131,6 +1116,7 @@ static int parse_attribute_from_arg(Item *item) { { 't', FS_NOTAIL_FL }, /* file tail should not be merged */ { 'T', FS_TOPDIR_FL }, /* Top of directory hierarchies */ { 'C', FS_NOCOW_FL }, /* Do not cow file */ + { 'P', FS_PROJINHERIT_FL }, /* Inherit the quota project ID */ }; enum { @@ -1183,7 +1169,7 @@ static int parse_attribute_from_arg(Item *item) { } if (mode == MODE_SET) - mask |= ATTRIBUTES_ALL; + mask |= CHATTR_ALL_FL; assert(mask != 0); @@ -1503,7 +1489,7 @@ typedef enum { _CREATION_MODE_INVALID = -1 } CreationMode; -static const char *creation_mode_verb_table[_CREATION_MODE_MAX] = { +static const char *const creation_mode_verb_table[_CREATION_MODE_MAX] = { [CREATION_NORMAL] = "Created", [CREATION_EXISTING] = "Found existing", [CREATION_FORCE] = "Created replacement", @@ -2255,7 +2241,7 @@ static int process_item(Item *i, OperationMask operation) { i->done |= operation; - r = chase_symlinks(i->path, NULL, CHASE_NO_AUTOFS|CHASE_WARN, NULL); + r = chase_symlinks(i->path, arg_root, CHASE_NO_AUTOFS|CHASE_WARN, NULL); if (r == -EREMOTE) { log_notice_errno(r, "Skipping %s", i->path); return 0; @@ -2521,7 +2507,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool return -EIO; } - if (!isempty(buffer) && !streq(buffer, "-")) { + if (!empty_or_dash(buffer)) { i.argument = strdup(buffer); if (!i.argument) return log_oom(); @@ -2718,7 +2704,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool free_and_replace(i.path, p); } - if (!isempty(user) && !streq(user, "-")) { + if (!empty_or_dash(user)) { const char *u = user; r = get_user_creds(&u, &i.uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); @@ -2730,7 +2716,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool i.uid_set = true; } - if (!isempty(group) && !streq(group, "-")) { + if (!empty_or_dash(group)) { const char *g = group; r = get_group_creds(&g, &i.gid, USER_CREDS_ALLOW_MISSING); @@ -2743,7 +2729,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool i.gid_set = true; } - if (!isempty(mode) && !streq(mode, "-")) { + if (!empty_or_dash(mode)) { const char *mm = mode; unsigned m; @@ -2763,7 +2749,7 @@ static int parse_line(const char *fname, unsigned line, const char *buffer, bool } else i.mode = IN_SET(i.type, CREATE_DIRECTORY, TRUNCATE_DIRECTORY, CREATE_SUBVOLUME, CREATE_SUBVOLUME_INHERIT_QUOTA, CREATE_SUBVOLUME_NEW_QUOTA) ? 0755 : 0644; - if (!isempty(age) && !streq(age, "-")) { + if (!empty_or_dash(age)) { const char *a = age; if (*a == '~') { @@ -3175,6 +3161,9 @@ static int run(int argc, char *argv[]) { log_setup_service(); + /* Descending down file system trees might take a lot of fds */ + (void) rlimit_nofile_bump(HIGH_RLIMIT_NOFILE); + if (arg_user) { r = user_config_paths(&config_dirs); if (r < 0) 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 fc165ffc0..271d0bb90 100644 --- a/src/tty-ask-password-agent/tty-ask-password-agent.c +++ b/src/tty-ask-password-agent/tty-ask-password-agent.c @@ -15,8 +15,10 @@ #include #include #include -#include +#include +#include #include +#include #include #include "alloc-util.h" @@ -31,8 +33,10 @@ #include "io-util.h" #include "macro.h" #include "main-func.h" +#include "memory-util.h" #include "mkdir.h" #include "path-util.h" +#include "plymouth-util.h" #include "pretty-print.h" #include "process-util.h" #include "signal-util.h" @@ -40,7 +44,6 @@ #include "string-util.h" #include "strv.h" #include "terminal-util.h" -#include "util.h" #include "utmp-wtmp.h" static enum { @@ -685,51 +688,53 @@ static int parse_argv(int argc, char *argv[]) { } /* - * To be able to ask on all terminal devices of /dev/console - * the devices are collected. If more than one device is found, - * then on each of the terminals a inquiring task is forked. - * Every task has its own session and its own controlling terminal. - * If one of the tasks does handle a password, the remaining tasks - * will be terminated. + * To be able to ask on all terminal devices of /dev/console the devices are collected. If more than one + * device is found, then on each of the terminals a inquiring task is forked. Every task has its own session + * and its own controlling terminal. If one of the tasks does handle a password, the remaining tasks will be + * terminated. */ -static int ask_on_this_console(const char *tty, pid_t *ret_pid, int argc, char *argv[]) { - struct sigaction sig = { +static int ask_on_this_console(const char *tty, pid_t *ret_pid, char **arguments) { + static const struct sigaction sigchld = { .sa_handler = nop_signal_handler, .sa_flags = SA_NOCLDSTOP | SA_RESTART, }; - pid_t pid; + static const struct sigaction sighup = { + .sa_handler = SIG_DFL, + .sa_flags = SA_RESTART, + }; int r; + assert_se(sigaction(SIGCHLD, &sigchld, NULL) >= 0); + assert_se(sigaction(SIGHUP, &sighup, NULL) >= 0); assert_se(sigprocmask_many(SIG_UNBLOCK, NULL, SIGHUP, SIGCHLD, -1) >= 0); - assert_se(sigemptyset(&sig.sa_mask) >= 0); - assert_se(sigaction(SIGCHLD, &sig, NULL) >= 0); - - sig.sa_handler = SIG_DFL; - assert_se(sigaction(SIGHUP, &sig, NULL) >= 0); - - r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, &pid); + r = safe_fork("(sd-passwd)", FORK_RESET_SIGNALS|FORK_LOG, ret_pid); if (r < 0) return r; if (r == 0) { - int ac; + char **i; assert_se(prctl(PR_SET_PDEATHSIG, SIGHUP) >= 0); - for (ac = 0; ac < argc; ac++) { - if (streq(argv[ac], "--console")) { - argv[ac] = strjoina("--console=", tty); - break; + STRV_FOREACH(i, arguments) { + char *k; + + if (!streq(*i, "--console")) + continue; + + k = strjoin("--console=", tty); + if (!k) { + log_oom(); + _exit(EXIT_FAILURE); } + + free_and_replace(*i, k); } - assert(ac < argc); - - execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, argv); + execv(SYSTEMD_TTY_ASK_PASSWORD_AGENT_BINARY_PATH, arguments); _exit(EXIT_FAILURE); } - *ret_pid = pid; return 0; } @@ -785,9 +790,9 @@ static void terminate_agents(Set *pids) { } } -static int ask_on_consoles(int argc, char *argv[]) { +static int ask_on_consoles(char *argv[]) { _cleanup_set_free_ Set *pids = NULL; - _cleanup_strv_free_ char **consoles = NULL; + _cleanup_strv_free_ char **consoles = NULL, **arguments = NULL; siginfo_t status = {}; char **tty; pid_t pid; @@ -801,9 +806,13 @@ static int ask_on_consoles(int argc, char *argv[]) { if (!pids) return log_oom(); + arguments = strv_copy(argv); + if (!arguments) + return log_oom(); + /* Start an agent on each console. */ STRV_FOREACH(tty, consoles) { - r = ask_on_this_console(*tty, &pid, argc, argv); + r = ask_on_this_console(*tty, &pid, arguments); if (r < 0) return r; @@ -848,7 +857,7 @@ static int run(int argc, char *argv[]) { /* * Spawn a separate process for each console device. */ - return ask_on_consoles(argc, argv); + return ask_on_consoles(argv); if (arg_device) { /* diff --git a/src/udev/ata_id/ata_id.c b/src/udev/ata_id/ata_id.c index 6c2233e43..8ea2e1e32 100644 --- a/src/udev/ata_id/ata_id.c +++ b/src/udev/ata_id/ata_id.c @@ -26,8 +26,8 @@ #include "fd-util.h" #include "libudev-util.h" #include "log.h" +#include "memory-util.h" #include "udev-util.h" -#include "util.h" #define COMMAND_TIMEOUT_MSEC (30 * 1000) diff --git a/src/udev/cdrom_id/cdrom_id.c b/src/udev/cdrom_id/cdrom_id.c index aed24a3b9..3f882f557 100644 --- a/src/udev/cdrom_id/cdrom_id.c +++ b/src/udev/cdrom_id/cdrom_id.c @@ -21,9 +21,9 @@ #include #include "log.h" +#include "memory-util.h" #include "random-util.h" #include "udev-util.h" -#include "util.h" /* device info */ static unsigned cd_cd_rom; @@ -585,7 +585,7 @@ out: static int cd_media_info(int fd) { struct scsi_cmd sc; unsigned char header[32]; - static const char *media_status[] = { + static const char *const media_status[] = { "blank", "appendable", "complete", diff --git a/src/udev/meson.build b/src/udev/meson.build index 9d3f6d1c5..01e4c09f5 100644 --- a/src/udev/meson.build +++ b/src/udev/meson.build @@ -180,6 +180,7 @@ foreach prog : [['ata_id/ata_id.c'], prog, include_directories : includes, c_args : ['-DLOG_REALM=LOG_REALM_UDEV'], + dependencies : [versiondep], link_with : [libudev_static], install_rpath : udev_rpath, install : true, @@ -197,3 +198,14 @@ configure_file( meson.add_install_script('sh', '-c', mkdir_p.format(join_paths(sysconfdir, 'udev/rules.d'))) + +fuzzers += [ + [['src/udev/net/fuzz-link-parser.c', + 'src/fuzz/fuzz.h'], + [libudev_core, + libudev_static, + libsystemd_network, + libshared], + [threads, + libacl]] + ] diff --git a/src/udev/net/ethtool-util.c b/src/udev/net/ethtool-util.c index 0dcec03f6..c94977e7b 100644 --- a/src/udev/net/ethtool-util.c +++ b/src/udev/net/ethtool-util.c @@ -9,11 +9,11 @@ #include "ethtool-util.h" #include "link-config.h" #include "log.h" +#include "memory-util.h" #include "missing.h" #include "socket-util.h" #include "string-table.h" #include "strxcpyx.h" -#include "util.h" static const char* const duplex_table[_DUP_MAX] = { [DUP_FULL] = "full", @@ -358,7 +358,7 @@ static int find_feature_index(struct ethtool_gstrings *strings, const char *feat return i; } - return -1; + return -ENODATA; } int ethtool_set_features(int *fd, const char *ifname, int *features) { @@ -775,7 +775,9 @@ int config_parse_advertise(const char *unit, break; mode = ethtool_link_mode_bit_from_string(w); - if (mode < 0) { + /* We reuse the kernel provided enum which does not contain negative value. So, the cast + * below is mandatory. Otherwise, the check below always passes and access an invalid address. */ + if ((int) mode < 0) { log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse advertise mode, ignoring: %s", w); continue; } diff --git a/src/udev/net/fuzz-link-parser.c b/src/udev/net/fuzz-link-parser.c new file mode 100644 index 000000000..e0dacc732 --- /dev/null +++ b/src/udev/net/fuzz-link-parser.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include "fd-util.h" +#include "fs-util.h" +#include "fuzz.h" +#include "link-config.h" +#include "tmpfile-util.h" + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL; + _cleanup_(unlink_tempfilep) char filename[] = "/tmp/fuzz-link-config.XXXXXX"; + _cleanup_fclose_ FILE *f = NULL; + + if (size > 65535) + return 0; + + if (!getenv("SYSTEMD_LOG_LEVEL")) + log_set_max_level(LOG_CRIT); + + assert_se(fmkostemp_safe(filename, "r+", &f) == 0); + if (size != 0) + assert_se(fwrite(data, size, 1, f) == 1); + + fflush(f); + assert_se(link_config_ctx_new(&ctx) >= 0); + (void) link_load_one(ctx, filename); + return 0; +} diff --git a/src/udev/net/fuzz-link-parser.options b/src/udev/net/fuzz-link-parser.options new file mode 100644 index 000000000..0824b19fa --- /dev/null +++ b/src/udev/net/fuzz-link-parser.options @@ -0,0 +1,2 @@ +[libfuzzer] +max_len = 65535 diff --git a/src/udev/net/link-config-gperf.gperf b/src/udev/net/link-config-gperf.gperf index 2bc18bff5..dff849a34 100644 --- a/src/udev/net/link-config-gperf.gperf +++ b/src/udev/net/link-config-gperf.gperf @@ -19,36 +19,36 @@ struct ConfigPerfItem; %struct-type %includes %% -Match.MACAddress, config_parse_hwaddrs, 0, offsetof(link_config, match_mac) -Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) -Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) -Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) -Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) -Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, match_host) -Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, match_virt) -Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, match_kernel_cmdline) -Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(link_config, match_kernel_version) -Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, match_arch) -Link.Description, config_parse_string, 0, offsetof(link_config, description) -Link.MACAddressPolicy, config_parse_mac_policy, 0, offsetof(link_config, mac_policy) -Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) -Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) -Link.Name, config_parse_ifname, 0, offsetof(link_config, name) -Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) -Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu) -Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) -Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) -Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation) -Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) -Link.Port, config_parse_port, 0, offsetof(link_config, port) -Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) -Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) -Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO6]) -Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0 -Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO]) -Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO]) -Link.RxChannels, config_parse_channel, 0, 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 +Match.MACAddress, config_parse_hwaddrs, 0, offsetof(link_config, match_mac) +Match.OriginalName, config_parse_ifnames, 0, offsetof(link_config, match_name) +Match.Path, config_parse_strv, 0, offsetof(link_config, match_path) +Match.Driver, config_parse_strv, 0, offsetof(link_config, match_driver) +Match.Type, config_parse_strv, 0, offsetof(link_config, match_type) +Match.Host, config_parse_net_condition, CONDITION_HOST, offsetof(link_config, conditions) +Match.Virtualization, config_parse_net_condition, CONDITION_VIRTUALIZATION, offsetof(link_config, conditions) +Match.KernelCommandLine, config_parse_net_condition, CONDITION_KERNEL_COMMAND_LINE, offsetof(link_config, conditions) +Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(link_config, conditions) +Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(link_config, conditions) +Link.Description, config_parse_string, 0, offsetof(link_config, description) +Link.MACAddressPolicy, config_parse_mac_address_policy, 0, offsetof(link_config, mac_address_policy) +Link.MACAddress, config_parse_hwaddr, 0, offsetof(link_config, mac) +Link.NamePolicy, config_parse_name_policy, 0, offsetof(link_config, name_policy) +Link.Name, config_parse_ifname, 0, offsetof(link_config, name) +Link.Alias, config_parse_ifalias, 0, offsetof(link_config, alias) +Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(link_config, mtu) +Link.BitsPerSecond, config_parse_si_size, 0, offsetof(link_config, speed) +Link.Duplex, config_parse_duplex, 0, offsetof(link_config, duplex) +Link.AutoNegotiation, config_parse_tristate, 0, offsetof(link_config, autonegotiation) +Link.WakeOnLan, config_parse_wol, 0, offsetof(link_config, wol) +Link.Port, config_parse_port, 0, offsetof(link_config, port) +Link.GenericSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GSO]) +Link.TCPSegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO]) +Link.TCP6SegmentationOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_TSO6]) +Link.UDPSegmentationOffload, config_parse_warn_compat, DISABLED_LEGACY, 0 +Link.GenericReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_GRO]) +Link.LargeReceiveOffload, config_parse_tristate, 0, offsetof(link_config, features[NET_DEV_FEAT_LRO]) +Link.RxChannels, config_parse_channel, 0, 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 eb2477cea..14690e364 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -8,11 +8,13 @@ #include "alloc-util.h" #include "conf-files.h" #include "conf-parser.h" +#include "def.h" #include "device-util.h" #include "ethtool-util.h" #include "fd-util.h" #include "link-config.h" #include "log.h" +#include "memory-util.h" #include "missing_network.h" #include "naming-scheme.h" #include "netlink-util.h" @@ -25,7 +27,6 @@ #include "string-table.h" #include "string-util.h" #include "strv.h" -#include "util.h" struct link_config_ctx { LIST_HEAD(link_config, links); @@ -36,18 +37,9 @@ struct link_config_ctx { sd_netlink *rtnl; - usec_t link_dirs_ts_usec; + usec_t network_dirs_ts_usec; }; -static const char* const link_dirs[] = { - "/etc/systemd/network", - "/run/systemd/network", - "/usr/lib/systemd/network", -#if HAVE_SPLIT_USR - "/lib/systemd/network", -#endif - NULL}; - static void link_config_free(link_config *link) { if (!link) return; @@ -58,12 +50,8 @@ static void link_config_free(link_config *link) { strv_free(link->match_path); strv_free(link->match_driver); strv_free(link->match_type); - free(link->match_name); - free(link->match_host); - free(link->match_virt); - free(link->match_kernel_cmdline); - free(link->match_kernel_version); - free(link->match_arch); + strv_free(link->match_name); + condition_free_list(link->conditions); free(link->description); free(link->mac); @@ -101,8 +89,6 @@ void link_config_ctx_free(link_config_ctx *ctx) { return; } -DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free); - int link_config_ctx_new(link_config_ctx **ret) { _cleanup_(link_config_ctx_freep) link_config_ctx *ctx = NULL; @@ -124,60 +110,63 @@ int link_config_ctx_new(link_config_ctx **ret) { return 0; } -static int load_link(link_config_ctx *ctx, const char *filename) { +int link_load_one(link_config_ctx *ctx, const char *filename) { _cleanup_(link_config_freep) link_config *link = NULL; _cleanup_fclose_ FILE *file = NULL; - int i; + _cleanup_free_ char *name = NULL; + size_t i; int r; assert(ctx); assert(filename); file = fopen(filename, "re"); - if (!file) { - if (errno == ENOENT) - return 0; - else - return -errno; - } + if (!file) + return errno == ENOENT ? 0 : -errno; if (null_or_empty_fd(fileno(file))) { log_debug("Skipping empty file: %s", filename); return 0; } - link = new0(link_config, 1); + name = strdup(filename); + if (!name) + return -ENOMEM; + + link = new(link_config, 1); if (!link) - return log_oom(); + return -ENOMEM; - link->mac_policy = _MACPOLICY_INVALID; - link->wol = _WOL_INVALID; - link->duplex = _DUP_INVALID; - link->port = _NET_DEV_PORT_INVALID; - link->autonegotiation = -1; + *link = (link_config) { + .filename = TAKE_PTR(name), + .mac_address_policy = _MAC_ADDRESS_POLICY_INVALID, + .wol = _WOL_INVALID, + .duplex = _DUP_INVALID, + .port = _NET_DEV_PORT_INVALID, + .autonegotiation = -1, + }; - for (i = 0; i < (int)ELEMENTSOF(link->features); i++) + for (i = 0; i < ELEMENTSOF(link->features); i++) link->features[i] = -1; r = config_parse(NULL, filename, file, - "Match\0Link\0Ethernet\0", + "Match\0Link\0", config_item_perf_lookup, link_config_gperf_lookup, CONFIG_PARSE_WARN, link); if (r < 0) return r; - else - log_debug("Parsed configuration file %s", filename); if (link->speed > UINT_MAX) return -ERANGE; - link->filename = strdup(filename); - if (!link->filename) - return log_oom(); + if (!condition_test_list(link->conditions, NULL, NULL, NULL)) { + log_debug("%s: Conditions do not match the system environment, skipping.", filename); + return 0; + } - LIST_PREPEND(links, ctx->links, link); - link = NULL; + log_debug("Parsed configuration file %s", filename); + LIST_PREPEND(links, ctx->links, TAKE_PTR(link)); return 0; } @@ -187,19 +176,19 @@ static bool enable_name_policy(void) { return proc_cmdline_get_bool("net.ifnames", &b) <= 0 || b; } -static int link_name_type(sd_device *device, unsigned *type) { +static int link_unsigned_attribute(sd_device *device, const char *attr, unsigned *type) { const char *s; int r; - r = sd_device_get_sysattr_value(device, "name_assign_type", &s); + r = sd_device_get_sysattr_value(device, attr, &s); if (r < 0) - return log_device_debug_errno(device, r, "Failed to query name_assign_type: %m"); + return log_device_debug_errno(device, r, "Failed to query %s: %m", attr); r = safe_atou(s, type); if (r < 0) - return log_device_warning_errno(device, r, "Failed to parse name_assign_type \"%s\": %m", s); + return log_device_warning_errno(device, r, "Failed to parse %s \"%s\": %m", attr, s); - log_device_debug(device, "Device has name_assign_type=%d", *type); + log_device_debug(device, "Device has %s=%u", attr, *type); return 0; } @@ -216,23 +205,23 @@ int link_config_load(link_config_ctx *ctx) { } /* update timestamp */ - paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, true); + paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, true); - r = conf_files_list_strv(&files, ".link", NULL, 0, link_dirs); + r = conf_files_list_strv(&files, ".link", NULL, 0, NETWORK_DIRS); if (r < 0) return log_error_errno(r, "failed to enumerate link files: %m"); STRV_FOREACH_BACKWARDS(f, files) { - r = load_link(ctx, *f); + r = link_load_one(ctx, *f); if (r < 0) - return r; + log_error_errno(r, "Failed to load %s, ignoring: %m", *f); } return 0; } bool link_config_should_reload(link_config_ctx *ctx) { - return paths_check_timestamp(link_dirs, &ctx->link_dirs_ts_usec, false); + return paths_check_timestamp(NETWORK_DIRS, &ctx->network_dirs_ts_usec, false); } int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) { @@ -243,33 +232,25 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) assert(ret); LIST_FOREACH(links, link, ctx->links) { - const char *address = NULL, *id_path = NULL, *parent_driver = NULL, *id_net_driver = NULL, *devtype = NULL, *sysname = NULL; - sd_device *parent; + const char *address = NULL, *id_path = NULL, *id_net_driver = NULL, *devtype = NULL, *sysname = NULL; (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, + link->match_type, link->match_name, 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; + unsigned name_assign_type = NET_NAME_UNKNOWN; - if (sd_device_get_sysattr_value(device, "name_assign_type", &attr_value) >= 0) - (void) safe_atou8(attr_value, &name_assign_type); + (void) link_unsigned_attribute(device, "name_assign_type", &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'", @@ -297,34 +278,41 @@ int link_config_get(link_config_ctx *ctx, sd_device *device, link_config **ret) return -ENOENT; } -static bool mac_is_random(sd_device *device) { - const char *s; - unsigned type; +static int get_mac(sd_device *device, MACAddressPolicy policy, struct ether_addr *mac) { + unsigned addr_type; + bool want_random = policy == MAC_ADDRESS_POLICY_RANDOM; int r; - /* if we can't get the assign type, assume it is not random */ - if (sd_device_get_sysattr_value(device, "addr_assign_type", &s) < 0) - return false; + assert(IN_SET(policy, MAC_ADDRESS_POLICY_RANDOM, MAC_ADDRESS_POLICY_PERSISTENT)); - r = safe_atou(s, &type); + r = link_unsigned_attribute(device, "addr_assign_type", &addr_type); if (r < 0) - return false; + return r; + switch (addr_type) { + case NET_ADDR_SET: + return log_device_debug(device, "MAC on the device already set by userspace"); + case NET_ADDR_STOLEN: + return log_device_debug(device, "MAC on the device already set based on another device"); + case NET_ADDR_RANDOM: + case NET_ADDR_PERM: + break; + default: + return log_device_warning(device, "Unknown addr_assign_type %u, ignoring", addr_type); + } - return type == NET_ADDR_RANDOM; -} + if (want_random == (addr_type == NET_ADDR_RANDOM)) + return log_device_debug(device, "MAC on the device already matches policy *%s*", + mac_address_policy_to_string(policy)); -static int get_mac(sd_device *device, bool want_random, - struct ether_addr *mac) { - int r; - - if (want_random) + if (want_random) { + log_device_debug(device, "Using random bytes to generate MAC"); random_bytes(mac->ether_addr_octet, ETH_ALEN); - else { + } else { uint64_t result; r = net_get_unique_predictable_data(device, &result); if (r < 0) - return r; + return log_device_warning_errno(device, r, "Could not generate persistent MAC: %m"); assert_cc(ETH_ALEN <= sizeof(result)); memcpy(mac->ether_addr_octet, &result, ETH_ALEN); @@ -333,8 +321,7 @@ static int get_mac(sd_device *device, bool want_random, /* see eth_random_addr in the kernel */ mac->ether_addr_octet[0] &= 0xfe; /* clear multicast bit */ mac->ether_addr_octet[0] |= 0x02; /* set local assignment bit (IEEE802) */ - - return 0; + return 1; } int link_config_apply(link_config_ctx *ctx, link_config *config, @@ -398,7 +385,7 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, return log_device_warning_errno(device, r, "Could not find ifindex: %m"); - (void) link_name_type(device, &name_type); + (void) link_unsigned_attribute(device, "name_assign_type", &name_type); if (IN_SET(name_type, NET_NAME_USER, NET_NAME_RENAMED) && !naming_scheme_has(NAMING_ALLOW_RERENAMES)) { @@ -455,33 +442,11 @@ int link_config_apply(link_config_ctx *ctx, link_config *config, log_device_debug(device, "Policies didn't yield a name and Name= is not given, not renaming."); no_rename: - switch (config->mac_policy) { - case MACPOLICY_PERSISTENT: - if (mac_is_random(device)) { - r = get_mac(device, false, &generated_mac); - if (r == -ENOENT) { - log_warning_errno(r, "Could not generate persistent MAC address for %s: %m", old_name); - break; - } else if (r < 0) - return r; - mac = &generated_mac; - } - break; - case MACPOLICY_RANDOM: - if (!mac_is_random(device)) { - r = get_mac(device, true, &generated_mac); - if (r == -ENOENT) { - log_warning_errno(r, "Could not generate random MAC address for %s: %m", old_name); - break; - } else if (r < 0) - return r; - mac = &generated_mac; - } - break; - case MACPOLICY_NONE: - default: - mac = config->mac; - } + if (IN_SET(config->mac_address_policy, MAC_ADDRESS_POLICY_PERSISTENT, MAC_ADDRESS_POLICY_RANDOM)) { + if (get_mac(device, config->mac_address_policy, &generated_mac) > 0) + mac = &generated_mac; + } else + mac = config->mac; r = rtnl_set_link_properties(&ctx->rtnl, ifindex, config->alias, mac, config->mtu); if (r < 0) @@ -509,14 +474,14 @@ int link_get_driver(link_config_ctx *ctx, sd_device *device, char **ret) { return 0; } -static const char* const mac_policy_table[_MACPOLICY_MAX] = { - [MACPOLICY_PERSISTENT] = "persistent", - [MACPOLICY_RANDOM] = "random", - [MACPOLICY_NONE] = "none", +static const char* const mac_address_policy_table[_MAC_ADDRESS_POLICY_MAX] = { + [MAC_ADDRESS_POLICY_PERSISTENT] = "persistent", + [MAC_ADDRESS_POLICY_RANDOM] = "random", + [MAC_ADDRESS_POLICY_NONE] = "none", }; -DEFINE_STRING_TABLE_LOOKUP(mac_policy, MACPolicy); -DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_policy, mac_policy, MACPolicy, +DEFINE_STRING_TABLE_LOOKUP(mac_address_policy, MACAddressPolicy); +DEFINE_CONFIG_PARSE_ENUM(config_parse_mac_address_policy, mac_address_policy, MACAddressPolicy, "Failed to parse MAC address policy"); static const char* const name_policy_table[_NAMEPOLICY_MAX] = { diff --git a/src/udev/net/link-config.h b/src/udev/net/link-config.h index 1113b1052..efe5f2ce3 100644 --- a/src/udev/net/link-config.h +++ b/src/udev/net/link-config.h @@ -12,13 +12,13 @@ typedef struct link_config_ctx link_config_ctx; typedef struct link_config link_config; -typedef enum MACPolicy { - MACPOLICY_PERSISTENT, - MACPOLICY_RANDOM, - MACPOLICY_NONE, - _MACPOLICY_MAX, - _MACPOLICY_INVALID = -1 -} MACPolicy; +typedef enum MACAddressPolicy { + MAC_ADDRESS_POLICY_PERSISTENT, + MAC_ADDRESS_POLICY_RANDOM, + MAC_ADDRESS_POLICY_NONE, + _MAC_ADDRESS_POLICY_MAX, + _MAC_ADDRESS_POLICY_INVALID = -1 +} MACAddressPolicy; typedef enum NamePolicy { NAMEPOLICY_KERNEL, @@ -40,15 +40,11 @@ struct link_config { char **match_driver; char **match_type; char **match_name; - Condition *match_host; - Condition *match_virt; - Condition *match_kernel_cmdline; - Condition *match_kernel_version; - Condition *match_arch; + LIST_HEAD(Condition, conditions); char *description; struct ether_addr *mac; - MACPolicy mac_policy; + MACAddressPolicy mac_address_policy; NamePolicy *name_policy; char *name; char *alias; @@ -67,7 +63,9 @@ struct link_config { int link_config_ctx_new(link_config_ctx **ret); void link_config_ctx_free(link_config_ctx *ctx); +DEFINE_TRIVIAL_CLEANUP_FUNC(link_config_ctx*, link_config_ctx_free); +int link_load_one(link_config_ctx *ctx, const char *filename); int link_config_load(link_config_ctx *ctx); bool link_config_should_reload(link_config_ctx *ctx); @@ -78,11 +76,11 @@ 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_; -const char *mac_policy_to_string(MACPolicy p) _const_; -MACPolicy mac_policy_from_string(const char *p) _pure_; +const char *mac_address_policy_to_string(MACAddressPolicy p) _const_; +MACAddressPolicy mac_address_policy_from_string(const char *p) _pure_; /* gperf lookup function */ const struct ConfigPerfItem* link_config_gperf_lookup(const char *key, GPERF_LEN_TYPE length); -CONFIG_PARSER_PROTOTYPE(config_parse_mac_policy); +CONFIG_PARSER_PROTOTYPE(config_parse_mac_address_policy); CONFIG_PARSER_PROTOTYPE(config_parse_name_policy); diff --git a/src/udev/scsi_id/scsi_serial.c b/src/udev/scsi_id/scsi_serial.c index c67d04747..7ca01858d 100644 --- a/src/udev/scsi_id/scsi_serial.c +++ b/src/udev/scsi_id/scsi_serial.c @@ -21,11 +21,11 @@ #include #include +#include "memory-util.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 diff --git a/src/udev/udev-builtin-usb_id.c b/src/udev/udev-builtin-usb_id.c index 3525d2504..7bdf6cfbb 100644 --- a/src/udev/udev-builtin-usb_id.c +++ b/src/udev/udev-builtin-usb_id.c @@ -138,13 +138,12 @@ static void set_scsi_type(char *to, const char *from, size_t len) { #define USB_DT_INTERFACE 0x04 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; + const char *filename, *syspath; int r; struct usb_interface_descriptor { uint8_t bLength; @@ -161,16 +160,17 @@ static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) { r = sd_device_get_syspath(dev, &syspath); if (r < 0) return r; - if (asprintf(&filename, "%s/descriptors", syspath) < 0) - return log_oom(); + filename = strjoina(syspath, "/descriptors"); fd = open(filename, O_RDONLY|O_CLOEXEC); if (fd < 0) - return log_device_debug_errno(dev, errno, "Failed to open USB device 'descriptors' file: %m"); + return log_device_debug_errno(dev, errno, "Failed to open \"%s\": %m", filename); size = read(fd, buf, sizeof(buf)); - if (size < 18 || (size_t) size >= sizeof(buf)) - return -EIO; + if (size < 18) + return log_device_warning_errno(dev, SYNTHETIC_ERRNO(EIO), + "Short read from \"%s\"", filename); + assert((size_t) size <= sizeof buf); ifs_str[0] = '\0'; while (pos + sizeof(struct usb_interface_descriptor) < (size_t) size && @@ -179,9 +179,12 @@ static int dev_if_packed_info(sd_device *dev, char *ifs_str, size_t len) { struct usb_interface_descriptor *desc; char if_str[8]; - desc = (struct usb_interface_descriptor *) &buf[pos]; + desc = (struct usb_interface_descriptor *) (buf + pos); if (desc->bLength < 3) break; + if (desc->bLength > size - sizeof(struct usb_interface_descriptor)) + return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EIO), + "Corrupt data read from \"%s\"", filename); pos += desc->bLength; if (desc->bDescriptorType != USB_DT_INTERFACE) diff --git a/src/udev/udev-builtin.c b/src/udev/udev-builtin.c index 48ce295a4..7bbfd143f 100644 --- a/src/udev/udev-builtin.c +++ b/src/udev/udev-builtin.c @@ -12,7 +12,7 @@ static bool initialized; -static const struct udev_builtin *builtins[_UDEV_BUILTIN_MAX] = { +static const struct udev_builtin *const builtins[_UDEV_BUILTIN_MAX] = { #if HAVE_BLKID [UDEV_BUILTIN_BLKID] = &udev_builtin_blkid, #endif diff --git a/src/udev/udev-ctrl.c b/src/udev/udev-ctrl.c index c217815ac..c3dc213aa 100644 --- a/src/udev/udev-ctrl.c +++ b/src/udev/udev-ctrl.c @@ -17,7 +17,10 @@ #include #include +#include "sd-event.h" + #include "alloc-util.h" +#include "errno-util.h" #include "fd-util.h" #include "format-util.h" #include "io-util.h" @@ -29,71 +32,53 @@ /* wire protocol magic must match */ #define UDEV_CTRL_MAGIC 0xdead1dea -enum udev_ctrl_msg_type { - UDEV_CTRL_UNKNOWN, - UDEV_CTRL_SET_LOG_LEVEL, - UDEV_CTRL_STOP_EXEC_QUEUE, - UDEV_CTRL_START_EXEC_QUEUE, - UDEV_CTRL_RELOAD, - UDEV_CTRL_SET_ENV, - UDEV_CTRL_SET_CHILDREN_MAX, - UDEV_CTRL_PING, - UDEV_CTRL_EXIT, -}; - struct udev_ctrl_msg_wire { char version[16]; unsigned magic; enum udev_ctrl_msg_type type; - union { - int intval; - char buf[256]; - }; -}; - -struct udev_ctrl_msg { - unsigned n_ref; - struct udev_ctrl_connection *conn; - struct udev_ctrl_msg_wire ctrl_msg_wire; + union udev_ctrl_msg_value value; }; struct udev_ctrl { unsigned n_ref; int sock; + int sock_connect; union sockaddr_union saddr; socklen_t addrlen; - bool bound; - bool cleanup_socket; - bool connected; + bool bound:1; + bool cleanup_socket:1; + bool connected:1; + bool maybe_disconnected:1; + sd_event *event; + sd_event_source *event_source; + sd_event_source *event_source_connect; + udev_ctrl_handler_t callback; + void *userdata; }; -struct udev_ctrl_connection { - unsigned n_ref; - struct udev_ctrl *uctrl; - int sock; -}; - -struct udev_ctrl *udev_ctrl_new_from_fd(int fd) { +int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd) { + _cleanup_close_ int sock = -1; struct udev_ctrl *uctrl; int r; - uctrl = new0(struct udev_ctrl, 1); - if (!uctrl) - return NULL; - uctrl->n_ref = 1; + assert(ret); if (fd < 0) { - uctrl->sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); - if (uctrl->sock < 0) { - log_error_errno(errno, "Failed to create socket: %m"); - udev_ctrl_unref(uctrl); - return NULL; - } - } else { - uctrl->bound = true; - uctrl->sock = fd; + sock = socket(AF_LOCAL, SOCK_SEQPACKET|SOCK_NONBLOCK|SOCK_CLOEXEC, 0); + if (sock < 0) + return log_error_errno(errno, "Failed to create socket: %m"); } + uctrl = new(struct udev_ctrl, 1); + if (!uctrl) + return -ENOMEM; + + *uctrl = (struct udev_ctrl) { + .n_ref = 1, + .sock = fd >= 0 ? fd : TAKE_FD(sock), + .bound = fd >= 0, + }; + /* * 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 @@ -108,45 +93,58 @@ struct udev_ctrl *udev_ctrl_new_from_fd(int fd) { }; uctrl->addrlen = SOCKADDR_UN_LEN(uctrl->saddr.un); - return uctrl; -} -struct udev_ctrl *udev_ctrl_new(void) { - return udev_ctrl_new_from_fd(-1); + *ret = TAKE_PTR(uctrl); + return 0; } int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl) { - int err; + int r; - if (!uctrl->bound) { - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - if (err < 0 && errno == EADDRINUSE) { - (void) sockaddr_un_unlink(&uctrl->saddr.un); - err = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); - } + assert(uctrl); - if (err < 0) - return log_error_errno(errno, "Failed to bind socket: %m"); + if (uctrl->bound) + return 0; - err = listen(uctrl->sock, 0); - if (err < 0) - return log_error_errno(errno, "Failed to listen: %m"); - - uctrl->bound = true; - uctrl->cleanup_socket = true; + r = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); + if (r < 0 && errno == EADDRINUSE) { + (void) sockaddr_un_unlink(&uctrl->saddr.un); + r = bind(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen); } + + if (r < 0) + return log_error_errno(errno, "Failed to bind udev control socket: %m"); + + if (listen(uctrl->sock, 0) < 0) + return log_error_errno(errno, "Failed to listen udev control socket: %m"); + + uctrl->bound = true; + uctrl->cleanup_socket = true; + return 0; } +static void udev_ctrl_disconnect(struct udev_ctrl *uctrl) { + if (!uctrl) + return; + + uctrl->event_source_connect = sd_event_source_unref(uctrl->event_source_connect); + uctrl->sock_connect = safe_close(uctrl->sock_connect); +} + static struct udev_ctrl *udev_ctrl_free(struct udev_ctrl *uctrl) { assert(uctrl); + udev_ctrl_disconnect(uctrl); + + sd_event_source_unref(uctrl->event_source); safe_close(uctrl->sock); + + sd_event_unref(uctrl->event); return mfree(uctrl); } -DEFINE_PRIVATE_TRIVIAL_REF_FUNC(struct udev_ctrl, udev_ctrl); -DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free); +DEFINE_TRIVIAL_REF_UNREF_FUNC(struct udev_ctrl, udev_ctrl, udev_ctrl_free); int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { if (!uctrl) @@ -156,142 +154,41 @@ int udev_ctrl_cleanup(struct udev_ctrl *uctrl) { return 0; } -int udev_ctrl_get_fd(struct udev_ctrl *uctrl) { - if (!uctrl) - return -EINVAL; - return uctrl->sock; -} - -struct udev_ctrl_connection *udev_ctrl_get_connection(struct udev_ctrl *uctrl) { - struct udev_ctrl_connection *conn; - struct ucred ucred = {}; +int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event) { int r; - conn = new(struct udev_ctrl_connection, 1); - if (!conn) - return NULL; - conn->n_ref = 1; - conn->uctrl = uctrl; + assert_return(uctrl, -EINVAL); + assert_return(!uctrl->event, -EBUSY); - conn->sock = accept4(uctrl->sock, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); - if (conn->sock < 0) { - if (errno != EINTR) - log_error_errno(errno, "Failed to receive ctrl connection: %m"); - goto err; + if (event) + uctrl->event = sd_event_ref(event); + else { + r = sd_event_default(&uctrl->event); + if (r < 0) + return r; } - /* check peer credential of connection */ - r = getpeercred(conn->sock, &ucred); - if (r < 0) { - 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); - goto err; - } - - /* enable receiving of the sender credentials in the messages */ - r = setsockopt_int(conn->sock, SOL_SOCKET, SO_PASSCRED, true); - if (r < 0) - log_warning_errno(r, "Failed to set SO_PASSCRED: %m"); - - udev_ctrl_ref(uctrl); - return conn; -err: - safe_close(conn->sock); - return mfree(conn); + return 0; } -static struct udev_ctrl_connection *udev_ctrl_connection_free(struct udev_ctrl_connection *conn) { - assert(conn); +sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl) { + assert(uctrl); - safe_close(conn->sock); - udev_ctrl_unref(conn->uctrl); - return mfree(conn); + return uctrl->event_source; } -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, usec_t timeout) { - struct udev_ctrl_msg_wire ctrl_msg_wire = { - .version = "udev-" STRINGIFY(PROJECT_VERSION), - .magic = UDEV_CTRL_MAGIC, - .type = type, - }; - - if (buf) - strscpy(ctrl_msg_wire.buf, sizeof(ctrl_msg_wire.buf), buf); - else - ctrl_msg_wire.intval = intval; - - if (!uctrl->connected) { - if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) - return -errno; - uctrl->connected = true; - } - if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) - return -errno; - - /* wait for peer message handling or disconnect */ - for (;;) { - struct pollfd pfd = { - .fd = uctrl->sock, - .events = POLLIN, - }; - int r; - - r = poll(&pfd, 1, DIV_ROUND_UP(timeout, USEC_PER_MSEC)); - if (r < 0) { - if (errno == EINTR) - continue; - return -errno; - } - if (r == 0) - return -ETIMEDOUT; - if (pfd.revents & POLLERR) - return -EIO; - return 0; - } +static void udev_ctrl_disconnect_and_listen_again(struct udev_ctrl *uctrl) { + udev_ctrl_disconnect(uctrl); + udev_ctrl_unref(uctrl); + (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_ON); } -int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL, timeout); -} +DEFINE_TRIVIAL_CLEANUP_FUNC(struct udev_ctrl *, udev_ctrl_disconnect_and_listen_again); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL, timeout); -} - -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL, timeout); -} - -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key, timeout); -} - -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL, timeout); -} - -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL, timeout); -} - -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout) { - return ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL, timeout); -} - -struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { - struct udev_ctrl_msg *uctrl_msg; - ssize_t size; - struct cmsghdr *cmsg; - struct iovec iov; +static int udev_ctrl_connection_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + _cleanup_(udev_ctrl_disconnect_and_listen_againp) struct udev_ctrl *uctrl = NULL; + struct udev_ctrl_msg_wire msg_wire; + struct iovec iov = IOVEC_MAKE(&msg_wire, sizeof(struct udev_ctrl_msg_wire)); char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; struct msghdr smsg = { .msg_iov = &iov, @@ -299,47 +196,28 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { .msg_control = cred_msg, .msg_controllen = sizeof(cred_msg), }; + struct cmsghdr *cmsg; struct ucred *cred; + ssize_t size; - uctrl_msg = new0(struct udev_ctrl_msg, 1); - if (!uctrl_msg) - return NULL; - uctrl_msg->n_ref = 1; - uctrl_msg->conn = conn; - udev_ctrl_connection_ref(conn); + assert(userdata); - /* wait for the incoming message */ - for (;;) { - struct pollfd pfd[1]; - int r; + /* When UDEV_CTRL_EXIT is received, manager unref udev_ctrl object. + * To avoid the object freed, let's increment the refcount. */ + uctrl = udev_ctrl_ref(userdata); - pfd[0].fd = conn->sock; - pfd[0].events = POLLIN; + size = next_datagram_size_fd(fd); + if (size < 0) + return log_error_errno(size, "Failed to get size of message: %m"); + if (size == 0) + return 0; /* Client disconnects? */ - r = poll(pfd, 1, 10000); - if (r < 0) { - if (errno == EINTR) - continue; - goto err; - } else if (r == 0) { - log_error("Timeout waiting for ctrl message"); - goto err; - } else { - if (!(pfd[0].revents & POLLIN)) { - log_error("Invalid ctrl connection: %m"); - goto err; - } - } - - break; - } - - iov = IOVEC_MAKE(&uctrl_msg->ctrl_msg_wire, sizeof(struct udev_ctrl_msg_wire)); - - size = recvmsg(conn->sock, &smsg, 0); + size = recvmsg(fd, &smsg, 0); if (size < 0) { - log_error_errno(errno, "Failed to receive ctrl message: %m"); - goto err; + if (errno != EINTR) + return log_error_errno(errno, "Failed to receive ctrl message: %m"); + + return 0; } cmsg_close_all(&smsg); @@ -348,80 +226,183 @@ struct udev_ctrl_msg *udev_ctrl_receive_msg(struct udev_ctrl_connection *conn) { if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS) { log_error("No sender credentials received, ignoring message"); - goto err; + return 0; } cred = (struct ucred *) CMSG_DATA(cmsg); if (cred->uid != 0) { - log_error("Sender uid="UID_FMT", ignoring message", cred->uid); - goto err; + log_error("Invalid sender uid "UID_FMT", ignoring message", cred->uid); + return 0; } - if (uctrl_msg->ctrl_msg_wire.magic != UDEV_CTRL_MAGIC) { - log_error("Message magic 0x%08x doesn't match, ignoring", uctrl_msg->ctrl_msg_wire.magic); - goto err; + if (msg_wire.magic != UDEV_CTRL_MAGIC) { + log_error("Message magic 0x%08x doesn't match, ignoring message", msg_wire.magic); + return 0; } - return uctrl_msg; -err: - udev_ctrl_msg_unref(uctrl_msg); - return NULL; + if (msg_wire.type == _UDEV_CTRL_END_MESSAGES) + return 0; + + if (uctrl->callback) + (void) uctrl->callback(uctrl, msg_wire.type, &msg_wire.value, uctrl->userdata); + + /* Do not disconnect and wait for next message. */ + uctrl = udev_ctrl_unref(uctrl); + return 0; } -static struct udev_ctrl_msg *udev_ctrl_msg_free(struct udev_ctrl_msg *ctrl_msg) { - assert(ctrl_msg); +static int udev_ctrl_event_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + struct udev_ctrl *uctrl = userdata; + _cleanup_close_ int sock = -1; + struct ucred ucred; + int r; - udev_ctrl_connection_unref(ctrl_msg->conn); - return mfree(ctrl_msg); + assert(uctrl); + + sock = accept4(fd, NULL, NULL, SOCK_CLOEXEC|SOCK_NONBLOCK); + if (sock < 0) { + if (ERRNO_IS_ACCEPT_AGAIN(errno)) + return 0; + + return log_error_errno(errno, "Failed to accept ctrl connection: %m"); + } + + /* check peer credential of connection */ + r = getpeercred(sock, &ucred); + if (r < 0) { + log_error_errno(r, "Failed to receive credentials of ctrl connection: %m"); + return 0; + } + + if (ucred.uid > 0) { + log_error("Invalid sender uid "UID_FMT", closing connection", ucred.uid); + return 0; + } + + /* enable receiving of the sender credentials in the messages */ + r = setsockopt_int(sock, SOL_SOCKET, SO_PASSCRED, true); + if (r < 0) + log_warning_errno(r, "Failed to set SO_PASSCRED, ignoring: %m"); + + r = sd_event_add_io(uctrl->event, &uctrl->event_source_connect, sock, EPOLLIN, udev_ctrl_connection_event_handler, uctrl); + if (r < 0) { + log_error_errno(r, "Failed to create event source for udev control connection: %m"); + return 0; + } + + (void) sd_event_source_set_description(uctrl->event_source_connect, "udev-ctrl-connection"); + + /* Do not accept multiple connection. */ + (void) sd_event_source_set_enabled(uctrl->event_source, SD_EVENT_OFF); + + uctrl->sock_connect = TAKE_FD(sock); + return 0; } -DEFINE_TRIVIAL_UNREF_FUNC(struct udev_ctrl_msg, udev_ctrl_msg, udev_ctrl_msg_free); +int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata) { + int r; -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; - return -1; + assert(uctrl); + + if (!uctrl->event) { + r = udev_ctrl_attach_event(uctrl, NULL); + if (r < 0) + return r; + } + + r = udev_ctrl_enable_receiving(uctrl); + if (r < 0) + return r; + + uctrl->callback = callback; + uctrl->userdata = userdata; + + r = sd_event_add_io(uctrl->event, &uctrl->event_source, uctrl->sock, EPOLLIN, udev_ctrl_event_handler, uctrl); + if (r < 0) + return r; + + (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl"); + + return 0; } -int udev_ctrl_get_stop_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_STOP_EXEC_QUEUE) - return 1; - return -1; +int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf) { + struct udev_ctrl_msg_wire ctrl_msg_wire = { + .version = "udev-" STRINGIFY(PROJECT_VERSION), + .magic = UDEV_CTRL_MAGIC, + .type = type, + }; + + if (uctrl->maybe_disconnected) + return -ENOANO; /* to distinguish this from other errors. */ + + if (buf) + strscpy(ctrl_msg_wire.value.buf, sizeof(ctrl_msg_wire.value.buf), buf); + else + ctrl_msg_wire.value.intval = intval; + + if (!uctrl->connected) { + if (connect(uctrl->sock, &uctrl->saddr.sa, uctrl->addrlen) < 0) + return -errno; + uctrl->connected = true; + } + + if (send(uctrl->sock, &ctrl_msg_wire, sizeof(ctrl_msg_wire), 0) < 0) + return -errno; + + if (type == UDEV_CTRL_EXIT) + uctrl->maybe_disconnected = true; + + return 0; } -int udev_ctrl_get_start_exec_queue(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_START_EXEC_QUEUE) - return 1; - return -1; +static int udev_ctrl_wait_io_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata) { + return sd_event_exit(sd_event_source_get_event(s), 0); } -int udev_ctrl_get_reload(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_RELOAD) - return 1; - return -1; -} +int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout) { + _cleanup_(sd_event_source_unrefp) sd_event_source *source_io = NULL, *source_timeout = NULL; + int r; -const char *udev_ctrl_get_set_env(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_ENV) - return ctrl_msg->ctrl_msg_wire.buf; - return NULL; -} + assert(uctrl); -int udev_ctrl_get_set_children_max(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_SET_CHILDREN_MAX) - return ctrl_msg->ctrl_msg_wire.intval; - return -1; -} + if (uctrl->sock < 0) + return 0; + if (!uctrl->connected) + return 0; -int udev_ctrl_get_ping(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_PING) - return 1; - return -1; -} + if (!uctrl->maybe_disconnected) { + r = udev_ctrl_send(uctrl, _UDEV_CTRL_END_MESSAGES, 0, NULL); + if (r < 0) + return r; + } -int udev_ctrl_get_exit(struct udev_ctrl_msg *ctrl_msg) { - if (ctrl_msg->ctrl_msg_wire.type == UDEV_CTRL_EXIT) - return 1; - return -1; + if (timeout == 0) + return 0; + + if (!uctrl->event) { + r = udev_ctrl_attach_event(uctrl, NULL); + if (r < 0) + return r; + } + + r = sd_event_add_io(uctrl->event, &source_io, uctrl->sock, EPOLLIN, udev_ctrl_wait_io_handler, NULL); + if (r < 0) + return r; + + (void) sd_event_source_set_description(uctrl->event_source, "udev-ctrl-wait-io"); + + if (timeout != USEC_INFINITY) { + usec_t usec; + + usec = now(clock_boottime_or_monotonic()) + timeout; + r = sd_event_add_time(uctrl->event, &source_timeout, clock_boottime_or_monotonic(), usec, 0, NULL, INT_TO_PTR(-ETIMEDOUT)); + if (r < 0) + return r; + + (void) sd_event_source_set_description(source_timeout, "udev-ctrl-wait-io"); + } + + return sd_event_loop(uctrl->event); } diff --git a/src/udev/udev-ctrl.h b/src/udev/udev-ctrl.h index 8e452a424..2c84a8b16 100644 --- a/src/udev/udev-ctrl.h +++ b/src/udev/udev-ctrl.h @@ -1,42 +1,79 @@ /* SPDX-License-Identifier: GPL-2.0+ */ #pragma once +#include "sd-event.h" + #include "macro.h" #include "time-util.h" struct udev_ctrl; -struct udev_ctrl *udev_ctrl_new(void); -struct udev_ctrl *udev_ctrl_new_from_fd(int fd); + +enum udev_ctrl_msg_type { + _UDEV_CTRL_END_MESSAGES, + UDEV_CTRL_SET_LOG_LEVEL, + UDEV_CTRL_STOP_EXEC_QUEUE, + UDEV_CTRL_START_EXEC_QUEUE, + UDEV_CTRL_RELOAD, + UDEV_CTRL_SET_ENV, + UDEV_CTRL_SET_CHILDREN_MAX, + UDEV_CTRL_PING, + UDEV_CTRL_EXIT, +}; + +union udev_ctrl_msg_value { + int intval; + char buf[256]; +}; + +typedef int (*udev_ctrl_handler_t)(struct udev_ctrl *udev_ctrl, enum udev_ctrl_msg_type type, + const union udev_ctrl_msg_value *value, void *userdata); + +int udev_ctrl_new_from_fd(struct udev_ctrl **ret, int fd); +static inline int udev_ctrl_new(struct udev_ctrl **ret) { + return udev_ctrl_new_from_fd(ret, -1); +} + int udev_ctrl_enable_receiving(struct udev_ctrl *uctrl); +struct udev_ctrl *udev_ctrl_ref(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, usec_t timeout); -int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_reload(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_ping(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_exit(struct udev_ctrl *uctrl, usec_t timeout); -int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key, usec_t timeout); -int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count, usec_t timeout); +int udev_ctrl_attach_event(struct udev_ctrl *uctrl, sd_event *event); +int udev_ctrl_start(struct udev_ctrl *uctrl, udev_ctrl_handler_t callback, void *userdata); +sd_event_source *udev_ctrl_get_event_source(struct udev_ctrl *uctrl); -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); +int udev_ctrl_wait(struct udev_ctrl *uctrl, usec_t timeout); -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); +int udev_ctrl_send(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, int intval, const char *buf); +static inline int udev_ctrl_send_set_log_level(struct udev_ctrl *uctrl, int priority) { + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_LOG_LEVEL, priority, NULL); +} + +static inline int udev_ctrl_send_stop_exec_queue(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_STOP_EXEC_QUEUE, 0, NULL); +} + +static inline int udev_ctrl_send_start_exec_queue(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_START_EXEC_QUEUE, 0, NULL); +} + +static inline int udev_ctrl_send_reload(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_RELOAD, 0, NULL); +} + +static inline int udev_ctrl_send_set_env(struct udev_ctrl *uctrl, const char *key) { + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_ENV, 0, key); +} + +static inline int udev_ctrl_send_set_children_max(struct udev_ctrl *uctrl, int count) { + return udev_ctrl_send(uctrl, UDEV_CTRL_SET_CHILDREN_MAX, count, NULL); +} + +static inline int udev_ctrl_send_ping(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_PING, 0, NULL); +} + +static inline int udev_ctrl_send_exit(struct udev_ctrl *uctrl) { + return udev_ctrl_send(uctrl, UDEV_CTRL_EXIT, 0, NULL); +} 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 07b7365e3..cab1b5ac0 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -28,6 +28,7 @@ #include "strxcpyx.h" #include "udev-builtin.h" #include "udev-node.h" +#include "udev-util.h" #include "udev-watch.h" #include "udev.h" @@ -71,8 +72,8 @@ UdevEvent *udev_event_free(UdevEvent *event) { sd_device_unref(event->dev); sd_device_unref(event->dev_db_clone); sd_netlink_unref(event->rtnl); - hashmap_free_free_key(event->run_list); - hashmap_free_free_free(event->seclabel_list); + ordered_hashmap_free_free_key(event->run_list); + ordered_hashmap_free_free_free(event->seclabel_list); free(event->program_result); free(event->name); @@ -151,13 +152,15 @@ static ssize_t subst_format_var(UdevEvent *event, break; case SUBST_KERNEL_NUMBER: r = sd_device_get_sysnum(dev, &val); + if (r == -ENOENT) + goto null_terminate; if (r < 0) - return r == -ENOENT ? 0 : r; + return r; l = strpcpy(&s, l, val); break; case SUBST_ID: if (!event->dev_parent) - return 0; + goto null_terminate; r = sd_device_get_sysname(event->dev_parent, &val); if (r < 0) return r; @@ -165,10 +168,12 @@ static ssize_t subst_format_var(UdevEvent *event, break; case SUBST_DRIVER: if (!event->dev_parent) - return 0; + goto null_terminate; r = sd_device_get_driver(event->dev_parent, &val); + if (r == -ENOENT) + goto null_terminate; if (r < 0) - return r == -ENOENT ? 0 : r; + return r; l = strpcpy(&s, l, val); break; case SUBST_MAJOR: @@ -187,7 +192,7 @@ static ssize_t subst_format_var(UdevEvent *event, int i; if (!event->program_result) - return 0; + goto null_terminate; /* get part of the result string */ i = 0; @@ -243,7 +248,7 @@ static ssize_t subst_format_var(UdevEvent *event, (void) sd_device_get_sysattr_value(event->dev_parent, attr, &val); if (!val) - return 0; + goto null_terminate; /* strip trailing whitespace, and replace unwanted characters */ if (val != vbuf) @@ -259,17 +264,23 @@ static ssize_t subst_format_var(UdevEvent *event, } case SUBST_PARENT: r = sd_device_get_parent(dev, &parent); + if (r == -ENODEV) + goto null_terminate; if (r < 0) - return r == -ENODEV ? 0 : r; + return r; r = sd_device_get_devname(parent, &val); + if (r == -ENOENT) + goto null_terminate; if (r < 0) - return r == -ENOENT ? 0 : r; + return r; l = strpcpy(&s, l, val + STRLEN("/dev/")); break; case SUBST_DEVNODE: r = sd_device_get_devname(dev, &val); + if (r == -ENOENT) + goto null_terminate; if (r < 0) - return r == -ENOENT ? 0 : r; + return r; l = strpcpy(&s, l, val); break; case SUBST_NAME: @@ -290,6 +301,8 @@ static ssize_t subst_format_var(UdevEvent *event, l = strpcpy(&s, l, val + STRLEN("/dev/")); else l = strpcpyl(&s, l, " ", val + STRLEN("/dev/"), NULL); + if (s == dest) + goto null_terminate; break; case SUBST_ROOT: l = strpcpy(&s, l, "/dev"); @@ -299,10 +312,12 @@ static ssize_t subst_format_var(UdevEvent *event, break; case SUBST_ENV: if (!attr) - return 0; + goto null_terminate; r = sd_device_get_property_value(dev, attr, &val); + if (r == -ENOENT) + goto null_terminate; if (r < 0) - return r == -ENOENT ? 0 : r; + return r; l = strpcpy(&s, l, val); break; default: @@ -310,6 +325,10 @@ static ssize_t subst_format_var(UdevEvent *event, } return s - dest; + +null_terminate: + *s = '\0'; + return 0; } ssize_t udev_event_apply_format(UdevEvent *event, @@ -677,8 +696,7 @@ int udev_event_spawn(UdevEvent *event, static int rename_netif(UdevEvent *event) { sd_device *dev = event->dev; - const char *action, *oldname; - char name[IFNAMSIZ]; + const char *oldname; int ifindex, r; if (!event->name) @@ -691,11 +709,7 @@ static int rename_netif(UdevEvent *event) { 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")) + if (!device_for_action(dev, DEVICE_ACTION_ADD)) return 0; /* Rename the interface only when it is added. */ r = sd_device_get_ifindex(dev, &ifindex); @@ -704,23 +718,27 @@ static int rename_netif(UdevEvent *event) { 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); + r = rtnl_set_link_name(&event->rtnl, ifindex, event->name); if (r < 0) - return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m", ifindex, oldname, name); + return log_device_error_errno(dev, r, "Failed to rename network interface %i from '%s' to '%s': %m", + ifindex, oldname, event->name); + + /* Set ID_RENAMING boolean property here, and drop it in the corresponding move uevent later. */ + r = device_add_property(dev, "ID_RENAMING", "1"); + if (r < 0) + log_device_warning_errno(dev, r, "Failed to add 'ID_RENAMING' property: %m"); r = device_rename(dev, event->name); if (r < 0) - 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_device_warning_errno(dev, r, "Failed to update properties with new name '%s': %m", event->name); - log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, name); + log_device_debug(dev, "Network interface %i is renamed from '%s' to '%s'", ifindex, oldname, event->name); return 1; } static int update_devnode(UdevEvent *event) { sd_device *dev = event->dev; - const char *action; bool apply; int r; @@ -760,11 +778,7 @@ static int update_devnode(UdevEvent *event) { } } - 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"); - - apply = streq(action, "add") || event->owner_set || event->group_set || event->mode_set; + apply = device_for_action(dev, DEVICE_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); } @@ -798,26 +812,48 @@ static void event_execute_rules_on_remove( (void) udev_node_remove(dev); } +static int udev_event_on_move(UdevEvent *event) { + sd_device *dev = event->dev; + int r; + + if (event->dev_db_clone && + sd_device_get_devnum(dev, NULL) < 0) { + r = device_copy_properties(dev, event->dev_db_clone); + if (r < 0) + log_device_debug_errno(dev, r, "Failed to copy properties from cloned sd_device object, ignoring: %m"); + } + + /* Drop previously added property */ + r = device_add_property(dev, "ID_RENAMING", NULL); + if (r < 0) + return log_device_debug_errno(dev, r, "Failed to remove 'ID_RENAMING' property, ignoring: %m"); + + return 0; +} + 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; + const char *subsystem; + DeviceAction action; + sd_device *dev; int r; assert(event); assert(rules); + dev = event->dev; + 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); + r = device_get_action(dev, &action); if (r < 0) - return log_device_error_errno(dev, r, "Failed to get property 'ACTION': %m"); + return log_device_error_errno(dev, r, "Failed to get ACTION: %m"); - if (streq(action, "remove")) { + if (action == DEVICE_ACTION_REMOVE) { event_execute_rules_on_remove(event, timeout_usec, properties_list, rules); return 0; } @@ -826,21 +862,12 @@ int udev_event_execute_rules(UdevEvent *event, 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 (event->dev_db_clone && sd_device_get_devnum(dev, NULL) >= 0) + /* Disable watch during event processing. */ + (void) udev_watch_end(event->dev_db_clone); - if (streq(action, "move")) { - r = device_copy_properties(dev, event->dev_db_clone); - if (r < 0) - 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 (action == DEVICE_ACTION_MOVE) + (void) udev_event_on_move(event); (void) udev_rules_apply_to_event(rules, event, timeout_usec, properties_list); @@ -873,7 +900,7 @@ void udev_event_execute_run(UdevEvent *event, usec_t timeout_usec) { void *val; Iterator i; - HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) { + ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) { enum udev_builtin_cmd builtin_cmd = PTR_TO_INT(val); char command[UTIL_PATH_SIZE]; diff --git a/src/udev/udev-node.c b/src/udev/udev-node.c index 1c00dd1e9..cfbbd7b28 100644 --- a/src/udev/udev-node.c +++ b/src/udev/udev-node.c @@ -272,7 +272,7 @@ int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) { static int node_permissions_apply(sd_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid, - Hashmap *seclabel_list) { + OrderedHashmap *seclabel_list) { const char *devnode, *subsystem, *id_filename = NULL; struct stat stats; dev_t devnum; @@ -318,7 +318,7 @@ static int node_permissions_apply(sd_device *dev, bool apply, log_device_debug(dev, "Preserve permissions of %s, %#o, uid=%u, gid=%u", devnode, mode, uid, gid); /* apply SECLABEL{$module}=$label */ - HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) { + ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list, i) { int q; if (streq(name, "selinux")) { @@ -386,7 +386,7 @@ static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) { int udev_node_add(sd_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid, - Hashmap *seclabel_list) { + OrderedHashmap *seclabel_list) { const char *devnode, *devlink; _cleanup_free_ char *filename = NULL; int r; diff --git a/src/udev/udev-node.h b/src/udev/udev-node.h index 223c8f0e4..5ae816d66 100644 --- a/src/udev/udev-node.h +++ b/src/udev/udev-node.h @@ -10,6 +10,6 @@ int udev_node_add(sd_device *dev, bool apply, mode_t mode, uid_t uid, gid_t gid, - Hashmap *seclabel_list); + OrderedHashmap *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 bc9c6c26c..ee87d7c65 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -15,6 +15,7 @@ #include "alloc-util.h" #include "conf-files.h" +#include "def.h" #include "device-private.h" #include "device-util.h" #include "dirent-util.h" @@ -41,6 +42,7 @@ #include "util.h" #define PREALLOC_TOKEN 2048 +#define RULES_DIRS (const char* const*) CONF_PATHS_STRV("udev/rules.d") struct uid_gid { unsigned name_off; @@ -50,32 +52,25 @@ struct uid_gid { }; }; -static const char* const rules_dirs[] = { - "/etc/udev/rules.d", - "/run/udev/rules.d", - UDEVLIBEXECDIR "/rules.d", - NULL -}; - struct UdevRules { usec_t dirs_ts_usec; ResolveNameTiming resolve_name_timing; /* every key in the rules file becomes a token */ struct token *tokens; - unsigned token_cur; - unsigned token_max; + size_t token_cur; + size_t 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 uids_cur; - unsigned uids_max; + size_t uids_cur; + size_t uids_max; struct uid_gid *gids; - unsigned gids_cur; - unsigned gids_max; + size_t gids_cur; + size_t gids_max; }; static char *rules_str(UdevRules *rules, unsigned off) { @@ -219,12 +214,12 @@ struct rule_tmp { UdevRules *rules; struct token rule; struct token token[MAX_TK]; - unsigned token_cur; + size_t token_cur; }; #if ENABLE_DEBUG_UDEV static const char *operation_str(enum operation_type type) { - static const char *operation_strs[] = { + static const char *const operation_strs[] = { [OP_UNSET] = "UNSET", [OP_MATCH] = "match", [OP_NOMATCH] = "nomatch", @@ -240,7 +235,7 @@ static const char *operation_str(enum operation_type type) { } static const char *string_glob_str(enum string_glob_type type) { - static const char *string_glob_strs[] = { + static const char *const string_glob_strs[] = { [GL_UNSET] = "UNSET", [GL_PLAIN] = "plain", [GL_GLOB] = "glob", @@ -253,7 +248,7 @@ static const char *string_glob_str(enum string_glob_type type) { } static const char *token_str(enum token_type type) { - static const char *token_strs[] = { + static const char *const token_strs[] = { [TK_UNSET] = "UNSET", [TK_RULE] = "RULE", @@ -430,9 +425,9 @@ static void dump_token(UdevRules *rules, struct token *token) { } static void dump_rules(UdevRules *rules) { - unsigned i; + size_t i; - log_debug("Dumping %u (%zu bytes) tokens, %zu (%zu bytes) strings", + log_debug("Dumping %zu (%zu bytes) tokens, %zu (%zu bytes) strings", rules->token_cur, rules->token_cur * sizeof(struct token), rules->strbuf->nodes_count, @@ -447,21 +442,9 @@ static void dump_rules(UdevRules *rules) {} 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 add; + if (!GREEDY_REALLOC(rules->tokens, rules->token_max, rules->token_cur + 1)) + return -ENOMEM; - /* double the buffer size */ - add = rules->token_max; - if (add < 8) - add = 8; - - tokens = reallocarray(rules->tokens, rules->token_max + add, sizeof(struct token)); - if (!tokens) - return -1; - rules->tokens = tokens; - rules->token_max += add; - } memcpy(&rules->tokens[rules->token_cur], token, sizeof(struct token)); rules->token_cur++; return 0; @@ -475,39 +458,25 @@ static void log_unknown_owner(sd_device *dev, int error, const char *entity, con } static uid_t add_uid(UdevRules *rules, const char *owner) { - unsigned i; uid_t uid = 0; unsigned off; + size_t i; int r; /* lookup, if we know it already */ for (i = 0; i < rules->uids_cur; i++) { off = rules->uids[i].name_off; - if (streq(rules_str(rules, off), owner)) { - uid = rules->uids[i].uid; - return uid; - } + if (streq(rules_str(rules, off), owner)) + return rules->uids[i].uid; } r = get_user_creds(&owner, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(NULL, r, "user", owner); /* grow buffer if needed */ - if (rules->uids_cur+1 >= rules->uids_max) { - struct uid_gid *uids; - unsigned add; + if (!GREEDY_REALLOC(rules->uids, rules->uids_max, rules->uids_cur + 1)) + return -ENOMEM; - /* double the buffer size */ - add = rules->uids_max; - if (add < 1) - add = 8; - - uids = reallocarray(rules->uids, rules->uids_max + add, sizeof(struct uid_gid)); - if (!uids) - return uid; - rules->uids = uids; - rules->uids_max += add; - } rules->uids[rules->uids_cur].uid = uid; off = rules_add_string(rules, owner); if (off <= 0) @@ -518,39 +487,25 @@ static uid_t add_uid(UdevRules *rules, const char *owner) { } static gid_t add_gid(UdevRules *rules, const char *group) { - unsigned i; gid_t gid = 0; unsigned off; + size_t i; int r; /* lookup, if we know it already */ for (i = 0; i < rules->gids_cur; i++) { off = rules->gids[i].name_off; - if (streq(rules_str(rules, off), group)) { - gid = rules->gids[i].gid; - return gid; - } + if (streq(rules_str(rules, off), group)) + return rules->gids[i].gid; } r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING); if (r < 0) log_unknown_owner(NULL, r, "group", group); /* grow buffer if needed */ - if (rules->gids_cur+1 >= rules->gids_max) { - struct uid_gid *gids; - unsigned add; + if (!GREEDY_REALLOC(rules->gids, rules->gids_max, rules->gids_cur + 1)) + return -ENOMEM; - /* double the buffer size */ - add = rules->gids_max; - if (add < 1) - add = 8; - - gids = reallocarray(rules->gids, rules->gids_max + add, sizeof(struct uid_gid)); - if (!gids) - return gid; - rules->gids = gids; - rules->gids_max += add; - } rules->gids[rules->gids_cur].gid = gid; off = rules_add_string(rules, group); if (off <= 0) @@ -647,7 +602,7 @@ static int import_program_into_properties(UdevEvent *event, char *line; int r; - r = udev_event_spawn(event, timeout_usec, false, program, result, sizeof result); + r = udev_event_spawn(event, timeout_usec, true, program, result, sizeof result); if (r < 0) return r; if (r > 0) @@ -716,11 +671,11 @@ static void attr_subst_subdir(char *attr, size_t len) { static int get_key(char **line, char **key, enum operation_type *op, char **value) { char *linepos; char *temp; - unsigned i, j; + size_t i, j; linepos = *line; if (!linepos || linepos[0] == '\0') - return -1; + return -EINVAL; /* skip whitespace */ while (isspace(linepos[0]) || linepos[0] == ',') @@ -728,13 +683,13 @@ static int get_key(char **line, char **key, enum operation_type *op, char **valu /* get the key */ if (linepos[0] == '\0') - return -1; + return -EINVAL; *key = linepos; for (;;) { linepos++; if (linepos[0] == '\0') - return -1; + return -EINVAL; if (isspace(linepos[0])) break; if (linepos[0] == '=') @@ -751,7 +706,7 @@ static int get_key(char **line, char **key, enum operation_type *op, char **valu while (isspace(linepos[0])) linepos++; if (linepos[0] == '\0') - return -1; + return -EINVAL; /* get operation type */ if (linepos[0] == '=' && linepos[1] == '=') { @@ -773,7 +728,7 @@ static int get_key(char **line, char **key, enum operation_type *op, char **valu *op = OP_ASSIGN_FINAL; linepos += 2; } else - return -1; + return -EINVAL; /* terminate key */ temp[0] = '\0'; @@ -782,13 +737,13 @@ static int get_key(char **line, char **key, enum operation_type *op, char **valu while (isspace(linepos[0])) linepos++; if (linepos[0] == '\0') - return -1; + return -EINVAL; /* get the value */ if (linepos[0] == '"') linepos++; else - return -1; + return -EINVAL; *value = linepos; /* terminate */ @@ -798,7 +753,7 @@ static int get_key(char **line, char **key, enum operation_type *op, char **valu break; if (linepos[i] == '\0') - return -1; + return -EINVAL; /* double quotes can be escaped */ if (linepos[i] == '\\') @@ -977,14 +932,15 @@ static int rule_add_key(struct rule_tmp *rule_tmp, enum token_type type, } static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) { - unsigned i; - unsigned start = 0; - unsigned end = rule_tmp->token_cur; + size_t i; + size_t start = 0; + size_t end = rule_tmp->token_cur; + int r; for (i = 0; i < rule_tmp->token_cur; i++) { enum token_type next_val = TK_UNSET; - unsigned next_idx = 0; - unsigned j; + size_t next_idx = 0; + size_t j; /* find smallest value */ for (j = start; j < end; j++) { @@ -997,8 +953,9 @@ static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) { } /* add token and mark done */ - if (add_token(rules, &rule_tmp->token[next_idx]) != 0) - return -1; + r = add_token(rules, &rule_tmp->token[next_idx]); + if (r < 0) + return r; rule_tmp->token[next_idx].type = TK_UNSET; /* shrink range */ @@ -1015,7 +972,7 @@ static int sort_token(UdevRules *rules, struct rule_tmp *rule_tmp) { #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; } -#define LOG_AND_RETURN_ADD_KEY LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %u items", rule_tmp.token_cur); +#define LOG_AND_RETURN_ADD_KEY LOG_AND_RETURN("Temporary rule array too small, aborting event processing with %zu items", rule_tmp.token_cur); static void add_rule(UdevRules *rules, char *line, const char *filename, unsigned filename_off, unsigned lineno) { @@ -1038,7 +995,7 @@ static void add_rule(UdevRules *rules, char *line, char *value; enum operation_type op; - if (get_key(&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(). */ @@ -1199,6 +1156,7 @@ static void add_rule(UdevRules *rules, char *line, else { if (STR_IN_SET(attr, "ACTION", + "SEQNUM", "SUBSYSTEM", "DEVTYPE", "MAJOR", @@ -1372,13 +1330,11 @@ static void add_rule(UdevRules *rules, char *line, } else if (streq(key, "OWNER")) { uid_t uid; - char *endptr; if (op == OP_REMOVE) LOG_AND_RETURN("Invalid %s operation", key); - uid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') + if (parse_uid(value, &uid) >= 0) r = rule_add_key(&rule_tmp, TK_A_OWNER_ID, op, NULL, &uid); else if (rules->resolve_name_timing == RESOLVE_NAME_EARLY && !strchr("$%", value[0])) { uid = add_uid(rules, value); @@ -1386,7 +1342,7 @@ static void add_rule(UdevRules *rules, char *line, } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER) r = rule_add_key(&rule_tmp, TK_A_OWNER, op, value, NULL); else { - LOG_RULE_ERROR("Invalid %s operation", key); + LOG_RULE_DEBUG("Resolving user name is disabled, ignoring %s=%s", key, value); continue; } if (r < 0) @@ -1396,13 +1352,11 @@ static void add_rule(UdevRules *rules, char *line, } else if (streq(key, "GROUP")) { gid_t gid; - char *endptr; if (op == OP_REMOVE) LOG_AND_RETURN("Invalid %s operation", key); - gid = strtoul(value, &endptr, 10); - if (endptr[0] == '\0') + if (parse_gid(value, &gid) >= 0) r = rule_add_key(&rule_tmp, TK_A_GROUP_ID, op, NULL, &gid); else if ((rules->resolve_name_timing == RESOLVE_NAME_EARLY) && !strchr("$%", value[0])) { gid = add_gid(rules, value); @@ -1410,7 +1364,7 @@ static void add_rule(UdevRules *rules, char *line, } else if (rules->resolve_name_timing != RESOLVE_NAME_NEVER) r = rule_add_key(&rule_tmp, TK_A_GROUP, op, value, NULL); else { - LOG_RULE_ERROR("Invalid %s operation", key); + LOG_RULE_DEBUG("Resolving group name is disabled, ignoring %s=%s", key, value); continue; } if (r < 0) @@ -1496,17 +1450,17 @@ static void add_rule(UdevRules *rules, char *line, /* 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) + if (add_token(rules, &rule_tmp.rule) < 0 || sort_token(rules, &rule_tmp) < 0) LOG_RULE_ERROR("Failed to add rule token"); } static int parse_file(UdevRules *rules, const char *filename) { + _cleanup_free_ char *continuation = NULL; _cleanup_fclose_ FILE *f = NULL; - unsigned first_token; + bool ignore_line = false; + size_t first_token, i; unsigned filename_off; - char line[UTIL_LINE_SIZE]; - int line_nr = 0; - unsigned i; + int line_nr = 0, r; f = fopen(filename, "re"); if (!f) { @@ -1525,46 +1479,66 @@ static int parse_file(UdevRules *rules, const char *filename) { first_token = rules->token_cur; filename_off = rules_add_string(rules, filename); - while (fgets(line, sizeof(line), f)) { - char *key; + for (;;) { + _cleanup_free_ char *buf = NULL; size_t len; + char *line; + + r = read_line(f, UTIL_LINE_SIZE, &buf); + if (r < 0) + return r; + if (r == 0) + break; - /* skip whitespace */ line_nr++; - key = line; - while (isspace(key[0])) - key++; + line = buf + strspn(buf, WHITESPACE); - /* comment */ - if (key[0] == '#') + if (line[0] == '#') continue; len = strlen(line); - if (len < 3) - continue; - /* continue reading if backslash+newline is found */ - while (line[len-2] == '\\') { - if (!fgets(&line[len-2], (sizeof(line)-len)+2, f)) - break; - if (strlen(&line[len-2]) < 2) - break; - line_nr++; - len = strlen(line); + if (continuation && !ignore_line) { + if (strlen(continuation) + len >= UTIL_LINE_SIZE) + ignore_line = true; + + if (!strextend(&continuation, line, NULL)) + return log_oom(); + + if (!ignore_line) { + line = continuation; + len = strlen(line); + } } - if (len+1 >= sizeof(line)) { + if (len > 0 && line[len - 1] == '\\') { + if (ignore_line) + continue; + + line[len - 1] = '\0'; + if (!continuation) { + continuation = strdup(line); + if (!continuation) + return log_oom(); + } + + continue; + } + + if (ignore_line) log_error("Line too long '%s':%u, ignored", filename, line_nr); - continue; - } - add_rule(rules, key, filename, filename_off, line_nr); + else if (len > 0) + add_rule(rules, line, filename, filename_off, line_nr); + + continuation = mfree(continuation); + ignore_line = false; } /* link GOTOs to LABEL rules in this file to be able to fast-forward */ 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 j; + size_t j; for (j = i+1; j < rules->token_cur; j++) { if (rules->tokens[j].type != TK_RULE) @@ -1600,7 +1574,7 @@ int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) }; /* init token array and string buffer */ - rules->tokens = malloc_multiply(PREALLOC_TOKEN, sizeof(struct token)); + rules->tokens = new(struct token, PREALLOC_TOKEN); if (!rules->tokens) return -ENOMEM; rules->token_max = PREALLOC_TOKEN; @@ -1611,7 +1585,7 @@ int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) udev_rules_check_timestamp(rules); - r = conf_files_list_strv(&files, ".rules", NULL, 0, rules_dirs); + r = conf_files_list_strv(&files, ".rules", NULL, 0, RULES_DIRS); if (r < 0) return log_error_errno(r, "Failed to enumerate rules files: %m"); @@ -1627,7 +1601,7 @@ int udev_rules_new(UdevRules **ret_rules, ResolveNameTiming resolve_name_timing) 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 (%zu * %zu bytes), %zu bytes strings", rules->token_max * sizeof(struct token), rules->token_max, sizeof(struct token), rules->strbuf->len); /* cleanup temporary strbuf data */ @@ -1663,20 +1637,19 @@ bool udev_rules_check_timestamp(UdevRules *rules) { if (!rules) return false; - return paths_check_timestamp(rules_dirs, &rules->dirs_ts_usec, true); + return paths_check_timestamp(RULES_DIRS, &rules->dirs_ts_usec, true); } -static int match_key(UdevRules *rules, struct token *token, const char *val) { +static bool 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) - val = ""; + val = strempty(val); switch (token->key.glob) { case GL_PLAIN: - match = (streq(key_value, val)); + match = streq(key_value, val); break; case GL_GLOB: match = (fnmatch(key_value, val, 0) == 0); @@ -1699,7 +1672,7 @@ static int match_key(UdevRules *rules, struct token *token, const char *val) { if (match) break; } else { - match = (streq(s, val)); + match = streq(s, val); break; } s = &next[1]; @@ -1729,17 +1702,13 @@ static int match_key(UdevRules *rules, struct token *token, const char *val) { match = (val[0] != '\0'); break; case GL_UNSET: - return -1; + return false; } - if (match && (token->key.op == OP_MATCH)) - return 0; - if (!match && (token->key.op == OP_NOMATCH)) - return 0; - return -1; + return token->key.op == (match ? OP_MATCH : OP_NOMATCH); } -static int match_attr(UdevRules *rules, sd_device *dev, UdevEvent *event, struct token *cur) { +static bool 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; @@ -1752,15 +1721,15 @@ static int match_attr(UdevRules *rules, sd_device *dev, UdevEvent *event, struct _fallthrough_; case SB_NONE: if (sd_device_get_sysattr_value(dev, name, &value) < 0) - return -1; + return false; break; case SB_SUBSYS: - if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) != 0) - return -1; + if (util_resolve_subsys_kernel(name, vbuf, sizeof(vbuf), true) < 0) + return false; value = vbuf; break; default: - return -1; + return false; } /* remove trailing whitespace, if not asked to match for it */ @@ -1798,18 +1767,19 @@ int udev_rules_apply_to_event( sd_device *dev = event->dev; enum escape_type esc = ESCAPE_UNSET; struct token *cur, *rule; - const char *action, *val; + DeviceAction action; + const char *val; bool can_set_name; int r; if (!rules->tokens) return 0; - r = sd_device_get_property_value(dev, "ACTION", &action); + r = device_get_action(dev, &action); if (r < 0) return r; - can_set_name = (!streq(action, "remove") && + can_set_name = (action != DEVICE_ACTION_REMOVE && (sd_device_get_devnum(dev, NULL) >= 0 || sd_device_get_ifindex(dev, NULL) >= 0)); @@ -1828,19 +1798,19 @@ int udev_rules_apply_to_event( esc = ESCAPE_UNSET; break; case TK_M_ACTION: - if (match_key(rules, cur, action) != 0) + if (!match_key(rules, cur, device_action_to_string(action))) goto nomatch; break; case TK_M_DEVPATH: if (sd_device_get_devpath(dev, &val) < 0) goto nomatch; - if (match_key(rules, cur, val) != 0) + if (!match_key(rules, cur, val)) goto nomatch; break; case TK_M_KERNEL: if (sd_device_get_sysname(dev, &val) < 0) goto nomatch; - if (match_key(rules, cur, val) != 0) + if (!match_key(rules, cur, val)) goto nomatch; break; case TK_M_DEVLINK: { @@ -1848,7 +1818,7 @@ int udev_rules_apply_to_event( bool match = false; FOREACH_DEVICE_DEVLINK(dev, devlink) - if (match_key(rules, cur, devlink + STRLEN("/dev/")) == 0) { + if (match_key(rules, cur, devlink + STRLEN("/dev/"))) { match = true; break; } @@ -1858,7 +1828,7 @@ int udev_rules_apply_to_event( break; } case TK_M_NAME: - if (match_key(rules, cur, event->name) != 0) + if (!match_key(rules, cur, event->name)) goto nomatch; break; case TK_M_ENV: { @@ -1872,7 +1842,7 @@ int udev_rules_apply_to_event( val = NULL; } - if (match_key(rules, cur, strempty(val))) + if (!match_key(rules, cur, strempty(val))) goto nomatch; break; } @@ -1894,17 +1864,17 @@ int udev_rules_apply_to_event( case TK_M_SUBSYSTEM: if (sd_device_get_subsystem(dev, &val) < 0) goto nomatch; - if (match_key(rules, cur, val) != 0) + if (!match_key(rules, cur, val)) goto nomatch; break; case TK_M_DRIVER: if (sd_device_get_driver(dev, &val) < 0) goto nomatch; - if (match_key(rules, cur, val) != 0) + if (!match_key(rules, cur, val)) goto nomatch; break; case TK_M_ATTR: - if (match_attr(rules, dev, event, cur) != 0) + if (!match_attr(rules, dev, event, cur)) goto nomatch; break; case TK_M_SYSCTL: { @@ -1920,7 +1890,7 @@ int udev_rules_apply_to_event( len = strlen(value); while (len > 0 && isspace(value[--len])) value[len] = '\0'; - if (match_key(rules, cur, value) != 0) + if (!match_key(rules, cur, value)) goto nomatch; break; } @@ -1948,23 +1918,23 @@ int udev_rules_apply_to_event( case TK_M_KERNELS: if (sd_device_get_sysname(event->dev_parent, &val) < 0) goto try_parent; - if (match_key(rules, key, val) != 0) + if (!match_key(rules, key, val)) goto try_parent; break; case TK_M_SUBSYSTEMS: if (sd_device_get_subsystem(event->dev_parent, &val) < 0) goto try_parent; - if (match_key(rules, key, val) != 0) + if (!match_key(rules, key, val)) goto try_parent; break; case TK_M_DRIVERS: if (sd_device_get_driver(event->dev_parent, &val) < 0) goto try_parent; - if (match_key(rules, key, val) != 0) + if (!match_key(rules, key, val)) goto try_parent; break; case TK_M_ATTRS: - if (match_attr(rules, event->dev_parent, event, key) != 0) + if (!match_attr(rules, event->dev_parent, event, key)) goto try_parent; break; case TK_M_TAGS: { @@ -1998,7 +1968,7 @@ int udev_rules_apply_to_event( int match; udev_event_apply_format(event, rules_str(rules, cur->key.value_off), filename, sizeof(filename), false); - if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), false) != 0) { + if (util_resolve_subsys_kernel(filename, filename, sizeof(filename), false) < 0) { if (filename[0] != '/') { char tmp[UTIL_PATH_SIZE]; @@ -2052,7 +2022,7 @@ int udev_rules_apply_to_event( 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(dev, import) != 0) + if (import_file_into_properties(dev, import) < 0) if (cur->key.op != OP_NOMATCH) goto nomatch; break; @@ -2066,7 +2036,7 @@ int udev_rules_apply_to_event( rules_str(rules, rule->rule.filename_off), rule->rule.filename_line); - if (import_program_into_properties(event, timeout_usec, import) != 0) + if (import_program_into_properties(event, timeout_usec, import) < 0) if (cur->key.op != OP_NOMATCH) goto nomatch; break; @@ -2146,13 +2116,13 @@ int udev_rules_apply_to_event( 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(dev, import) != 0) + if (import_parent_into_properties(dev, import) < 0) if (cur->key.op != OP_NOMATCH) goto nomatch; break; } case TK_M_RESULT: - if (match_key(rules, cur, event->program_result) != 0) + if (!match_key(rules, cur, event->program_result)) goto nomatch; break; case TK_A_STRING_ESCAPE_NONE: @@ -2291,13 +2261,13 @@ int udev_rules_apply_to_event( return log_oom(); if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL)) - hashmap_clear_free_free(event->seclabel_list); + ordered_hashmap_clear_free_free(event->seclabel_list); - r = hashmap_ensure_allocated(&event->seclabel_list, NULL); + r = ordered_hashmap_ensure_allocated(&event->seclabel_list, NULL); if (r < 0) return log_oom(); - r = hashmap_put(event->seclabel_list, name, label); + r = ordered_hashmap_put(event->seclabel_list, name, label); if (r < 0) return log_oom(); log_device_debug(dev, "SECLABEL{%s}='%s' %s:%u", @@ -2440,7 +2410,7 @@ int udev_rules_apply_to_event( const char *key_name; key_name = rules_str(rules, cur->key.attr_off); - if (util_resolve_subsys_kernel(key_name, attr, sizeof(attr), false) != 0 && + 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)); @@ -2474,9 +2444,9 @@ int udev_rules_apply_to_event( _cleanup_free_ char *cmd = NULL; if (IN_SET(cur->key.op, OP_ASSIGN, OP_ASSIGN_FINAL)) - hashmap_clear_free_key(event->run_list); + ordered_hashmap_clear_free_key(event->run_list); - r = hashmap_ensure_allocated(&event->run_list, NULL); + r = ordered_hashmap_ensure_allocated(&event->run_list, NULL); if (r < 0) return log_oom(); @@ -2484,7 +2454,7 @@ int udev_rules_apply_to_event( if (!cmd) return log_oom(); - r = hashmap_put(event->run_list, cmd, INT_TO_PTR(cur->key.builtin_cmd)); + r = ordered_hashmap_put(event->run_list, cmd, INT_TO_PTR(cur->key.builtin_cmd)); if (r < 0) return log_oom(); @@ -2581,7 +2551,7 @@ int udev_rules_apply_static_dev_perms(UdevRules *rules) { goto next; strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL); - if (stat(device_node, &stats) != 0) + if (stat(device_node, &stats) < 0) break; if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode)) break; diff --git a/src/udev/udev.h b/src/udev/udev.h index 3bc69ff6c..2fb49dc97 100644 --- a/src/udev/udev.h +++ b/src/udev/udev.h @@ -25,8 +25,8 @@ typedef struct UdevEvent { mode_t mode; uid_t uid; gid_t gid; - Hashmap *seclabel_list; - Hashmap *run_list; + OrderedHashmap *seclabel_list; + OrderedHashmap *run_list; usec_t exec_delay_usec; usec_t birth_usec; sd_netlink *rtnl; diff --git a/src/udev/udevadm-control.c b/src/udev/udevadm-control.c index 7cfc4c929..f9b3e9579 100644 --- a/src/udev/udevadm-control.c +++ b/src/udev/udevadm-control.c @@ -86,48 +86,60 @@ int control_main(int argc, char *argv[], void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "This command expects one or more options."); - uctrl = udev_ctrl_new(); - if (!uctrl) - return log_oom(); + r = udev_ctrl_new(&uctrl); + if (r < 0) + return log_error_errno(r, "Failed to initialize udev control: %m"); while ((c = getopt_long(argc, argv, "el:sSRp:m:t:Vh", options, NULL)) >= 0) switch (c) { case 'e': - r = udev_ctrl_send_exit(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_exit(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --exit after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send exit request: %m"); break; case 'l': r = log_level_from_string(optarg); if (r < 0) return log_error_errno(r, "Failed to parse log priority '%s': %m", optarg); - r = udev_ctrl_send_set_log_level(uctrl, r, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_set_log_level(uctrl, r); + if (r == -ENOANO) + log_warning("Cannot specify --log-priority after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to set log level: %m"); break; case 's': - r = udev_ctrl_send_stop_exec_queue(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_stop_exec_queue(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --stop-exec-queue after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to stop exec queue: %m"); break; case 'S': - r = udev_ctrl_send_start_exec_queue(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_start_exec_queue(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --start-exec-queue after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to start exec queue: %m"); break; case 'R': - r = udev_ctrl_send_reload(uctrl, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_reload(uctrl); + if (r == -ENOANO) + log_warning("Cannot specify --reload after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send reload request: %m"); break; case 'p': if (!strchr(optarg, '=')) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "expect = instead of '%s'", optarg); - r = udev_ctrl_send_set_env(uctrl, optarg, timeout); - if (r < 0) - return r; + r = udev_ctrl_send_set_env(uctrl, optarg); + if (r == -ENOANO) + log_warning("Cannot specify --property after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to update environment: %m"); break; case 'm': { unsigned i; @@ -136,15 +148,19 @@ int control_main(int argc, char *argv[], void *userdata) { 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; + r = udev_ctrl_send_set_children_max(uctrl, i); + if (r == -ENOANO) + log_warning("Cannot specify --children-max after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send request to set number of children: %m"); break; } case ARG_PING: - r = udev_ctrl_send_ping(uctrl, timeout); - if (r < 0) - return log_error_errno(r, "Failed to connect to udev daemon: %m"); + r = udev_ctrl_send_ping(uctrl); + if (r == -ENOANO) + log_error("Cannot specify --ping after --exit, ignoring."); + else if (r < 0) + return log_error_errno(r, "Failed to send a ping message: %m"); break; case 't': r = parse_sec(optarg, &timeout); @@ -165,5 +181,9 @@ int control_main(int argc, char *argv[], void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Extraneous argument: %s", argv[optind]); + r = udev_ctrl_wait(uctrl, timeout); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); + return 0; } diff --git a/src/udev/udevadm-info.c b/src/udev/udevadm-info.c index ebd15d384..6742cda8f 100644 --- a/src/udev/udevadm-info.c +++ b/src/udev/udevadm-info.c @@ -18,9 +18,10 @@ #include "device-util.h" #include "dirent-util.h" #include "fd-util.h" +#include "string-table.h" #include "string-util.h" -#include "udevadm.h" #include "udevadm-util.h" +#include "udevadm.h" typedef enum ActionType { ACTION_QUERY, @@ -50,12 +51,8 @@ static bool skip_attribute(const char *name) { "subsystem", "module", }; - unsigned i; - for (i = 0; i < ELEMENTSOF(skip); i++) - if (streq(name, skip[i])) - return true; - return false; + return string_table_lookup(skip, ELEMENTSOF(skip), name) >= 0; } static void print_all_attributes(sd_device *device, const char *key) { @@ -361,7 +358,7 @@ int info_main(int argc, char *argv[], void *userdata) { ActionType action = ACTION_QUERY; QueryType query = QUERY_ALL; - while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:RVh", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "aced:n:p:q:rxP:Vh", options, NULL)) >= 0) switch (c) { case 'n': case 'p': { diff --git a/src/udev/udevadm-monitor.c b/src/udev/udevadm-monitor.c index 3dde3f338..ca7ca3c37 100644 --- a/src/udev/udevadm-monitor.c +++ b/src/udev/udevadm-monitor.c @@ -9,6 +9,7 @@ #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" @@ -18,6 +19,7 @@ #include "string-util.h" #include "udevadm.h" #include "virt.h" +#include "time-util.h" static bool arg_show_property = false; static bool arg_print_kernel = false; @@ -26,14 +28,15 @@ 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; + DeviceAction action = _DEVICE_ACTION_INVALID; + const char *devpath = NULL, *subsystem = NULL; MonitorNetlinkGroup group = PTR_TO_INT(userdata); struct timespec ts; assert(device); assert(IN_SET(group, MONITOR_GROUP_UDEV, MONITOR_GROUP_KERNEL)); - (void) sd_device_get_property_value(device, "ACTION", &action); + (void) device_get_action(device, &action); (void) sd_device_get_devpath(device, &devpath); (void) sd_device_get_subsystem(device, &subsystem); @@ -42,7 +45,8 @@ static int device_monitor_handler(sd_device_monitor *monitor, sd_device *device, printf("%-6s[%"PRI_TIME".%06"PRI_NSEC"] %-8s %s (%s)\n", group == MONITOR_GROUP_UDEV ? "UDEV" : "KERNEL", ts.tv_sec, (nsec_t)ts.tv_nsec/1000, - action, devpath, subsystem); + strna(device_action_to_string(action)), + devpath, subsystem); if (arg_show_property) { const char *key, *value; diff --git a/src/udev/udevadm-settle.c b/src/udev/udevadm-settle.c index 8d9c4509f..9cf88b245 100644 --- a/src/udev/udevadm-settle.c +++ b/src/udev/udevadm-settle.c @@ -100,13 +100,16 @@ int settle_main(int argc, char *argv[], void *userdata) { if (getuid() == 0) { _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; - uctrl = udev_ctrl_new(); - if (uctrl) { - r = udev_ctrl_send_ping(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout)); + if (udev_ctrl_new(&uctrl) >= 0) { + r = udev_ctrl_send_ping(uctrl); if (r < 0) { log_debug_errno(r, "Failed to connect to udev daemon: %m"); return 0; } + + r = udev_ctrl_wait(uctrl, MAX(5 * USEC_PER_SEC, arg_timeout)); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); } } diff --git a/src/udev/udevadm-test.c b/src/udev/udevadm-test.c index 54c525e02..da4c4cb87 100644 --- a/src/udev/udevadm-test.c +++ b/src/udev/udevadm-test.c @@ -53,9 +53,17 @@ static int parse_argv(int argc, char *argv[]) { while ((c = getopt_long(argc, argv, "a:N:Vh", options, NULL)) >= 0) switch (c) { - case 'a': + case 'a': { + DeviceAction a; + + a = device_action_from_string(optarg); + if (a < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), + "Invalid action '%s'", optarg); + arg_action = optarg; break; + } case 'N': arg_resolve_name_timing = resolve_name_timing_from_string(optarg); if (arg_resolve_name_timing < 0) @@ -135,7 +143,7 @@ int test_main(int argc, char *argv[], void *userdata) { FOREACH_DEVICE_PROPERTY(dev, key, value) printf("%s=%s\n", key, value); - HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) { + ORDERED_HASHMAP_FOREACH_KEY(val, cmd, event->run_list, i) { char program[UTIL_PATH_SIZE]; udev_event_apply_format(event, cmd, program, sizeof(program), false); diff --git a/src/udev/udevadm-trigger.c b/src/udev/udevadm-trigger.c index 95329469e..b7dafb775 100644 --- a/src/udev/udevadm-trigger.c +++ b/src/udev/udevadm-trigger.c @@ -7,6 +7,7 @@ #include "sd-event.h" #include "device-enumerator-private.h" +#include "device-private.h" #include "fd-util.h" #include "fileio.h" #include "path-util.h" @@ -59,6 +60,7 @@ static int exec_list(sd_device_enumerator *e, const char *action, Set *settle_se } static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *userdata) { + _cleanup_free_ char *val = NULL; Set *settle_set = userdata; const char *syspath; @@ -71,7 +73,8 @@ static int device_monitor_handler(sd_device_monitor *m, sd_device *dev, void *us if (arg_verbose) printf("settle %s\n", syspath); - if (!set_remove(settle_set, syspath)) + val = set_remove(settle_set, syspath); + if (!val) log_debug("Got epoll event on syspath %s not present in syspath set", syspath); if (set_isempty(settle_set)) @@ -199,11 +202,10 @@ int trigger_main(int argc, char *argv[], void *userdata) { return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown type --type=%s", optarg); break; case 'c': - if (STR_IN_SET(optarg, "add", "remove", "change")) - action = optarg; - else - log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg); + if (device_action_from_string(optarg) < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Unknown action '%s'", optarg); + action = optarg; break; case 's': r = sd_device_enumerator_add_match_subsystem(e, optarg, true); @@ -256,7 +258,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to open the device '%s': %m", optarg); - r = sd_device_enumerator_add_match_parent(e, dev); + r = device_enumerator_add_match_parent_incremental(e, dev); if (r < 0) return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); break; @@ -272,7 +274,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to open the device '%s': %m", optarg); - r = sd_device_enumerator_add_match_parent(e, dev); + r = device_enumerator_add_match_parent_incremental(e, dev); if (r < 0) return log_error_errno(r, "Failed to add parent match '%s': %m", optarg); break; @@ -308,13 +310,17 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (ping) { _cleanup_(udev_ctrl_unrefp) struct udev_ctrl *uctrl = NULL; - uctrl = udev_ctrl_new(); - if (!uctrl) - return log_oom(); + r = udev_ctrl_new(&uctrl); + if (r < 0) + return log_error_errno(r, "Failed to initialize udev control: %m"); - r = udev_ctrl_send_ping(uctrl, ping_timeout_usec); + r = udev_ctrl_send_ping(uctrl); if (r < 0) return log_error_errno(r, "Failed to connect to udev daemon: %m"); + + r = udev_ctrl_wait(uctrl, ping_timeout_usec); + if (r < 0) + return log_error_errno(r, "Failed to wait for daemon to reply: %m"); } for (; optind < argc; optind++) { @@ -324,7 +330,7 @@ int trigger_main(int argc, char *argv[], void *userdata) { if (r < 0) return log_error_errno(r, "Failed to open the device '%s': %m", argv[optind]); - r = sd_device_enumerator_add_match_parent(e, dev); + r = device_enumerator_add_match_parent_incremental(e, dev); if (r < 0) return log_error_errno(r, "Failed to add parent match '%s': %m", argv[optind]); } diff --git a/src/udev/udevadm.c b/src/udev/udevadm.c index 28194314d..e6dbb111a 100644 --- a/src/udev/udevadm.c +++ b/src/udev/udevadm.c @@ -16,7 +16,7 @@ #include "util.h" static int help(void) { - static const char * short_descriptions[][2] = { + static const char *const short_descriptions[][2] = { { "info", "Query sysfs or the udev database" }, { "trigger", "Request events from the kernel" }, { "settle", "Wait for pending udev events" }, diff --git a/src/udev/udevd.c b/src/udev/udevd.c index 0303f36b4..140ec3529 100644 --- a/src/udev/udevd.c +++ b/src/udev/udevd.c @@ -46,6 +46,7 @@ #include "hashmap.h" #include "io-util.h" #include "libudev-device-internal.h" +#include "limits-util.h" #include "list.h" #include "main-func.h" #include "mkdir.h" @@ -89,12 +90,9 @@ typedef struct Manager { sd_device_monitor *monitor; struct udev_ctrl *ctrl; - struct udev_ctrl_connection *ctrl_conn_blocking; int fd_inotify; int worker_watch[2]; - sd_event_source *ctrl_event; - sd_event_source *uevent_event; sd_event_source *inotify_event; sd_event_source *kill_workers_event; @@ -278,8 +276,6 @@ static void worker_attach_event(struct worker *worker, struct event *event) { static void manager_clear_for_worker(Manager *manager) { assert(manager); - 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); @@ -289,7 +285,6 @@ static void manager_clear_for_worker(Manager *manager) { event_queue_cleanup(manager, EVENT_UNDEF); 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]); @@ -301,6 +296,9 @@ static void manager_free(Manager *manager) { udev_builtin_exit(); + if (manager->pid == getpid_cached()) + udev_ctrl_cleanup(manager->ctrl); + manager_clear_for_worker(manager); sd_netlink_unref(manager->rtnl); @@ -339,11 +337,7 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { * 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")) + if (device_for_action(dev, DEVICE_ACTION_REMOVE)) return 0; r = sd_device_get_subsystem(dev, &val); @@ -391,21 +385,23 @@ static int worker_lock_block_device(sd_device *dev, int *ret_fd) { 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, *action; + DeviceAction action; + uint64_t seqnum; int r; assert(manager); assert(dev); - r = sd_device_get_property_value(dev, "SEQNUM", &seqnum); + r = device_get_seqnum(dev, &seqnum); if (r < 0) return log_device_debug_errno(dev, r, "Failed to get SEQNUM: %m"); - r = sd_device_get_property_value(dev, "ACTION", &action); + r = device_get_action(dev, &action); if (r < 0) return log_device_debug_errno(dev, r, "Failed to get ACTION: %m"); - log_device_debug(dev, "Processing device (SEQNUM=%s, ACTION=%s)", seqnum, action); + log_device_debug(dev, "Processing device (SEQNUM=%"PRIu64", ACTION=%s)", + seqnum, device_action_to_string(action)); udev_event = udev_event_new(dev, arg_exec_delay_usec, manager->rtnl); if (!udev_event) @@ -431,7 +427,8 @@ static int worker_process_device(Manager *manager, sd_device *dev) { return log_device_debug_errno(dev, r, "Failed to update database under /run/udev/data/: %m"); } - log_device_debug(dev, "Device (SEQNUM=%s, ACTION=%s) processed", seqnum, action); + log_device_debug(dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) processed", + seqnum, device_action_to_string(action)); return 0; } @@ -587,8 +584,8 @@ static void event_run(Manager *manager, struct event *event) { static int event_queue_insert(Manager *manager, sd_device *dev) { _cleanup_(sd_device_unrefp) sd_device *clone = NULL; - const char *val, *action; struct event *event; + DeviceAction action; uint64_t seqnum; int r; @@ -596,25 +593,15 @@ static int event_queue_insert(Manager *manager, sd_device *dev) { assert(dev); /* only one process can add events to the queue */ - if (manager->pid == 0) - manager->pid = getpid_cached(); - assert(manager->pid == getpid_cached()); /* We only accepts devices received by device monitor. */ - r = sd_device_get_property_value(dev, "SEQNUM", &val); + r = device_get_seqnum(dev, &seqnum); if (r < 0) return r; - r = safe_atou64(val, &seqnum); - if (r < 0) - return r; - - if (seqnum == 0) - return -EINVAL; - /* Refuse devices do not have ACTION property. */ - r = sd_device_get_property_value(dev, "ACTION", &action); + r = device_get_action(dev, &action); if (r < 0) return r; @@ -647,7 +634,8 @@ static int event_queue_insert(Manager *manager, sd_device *dev) { LIST_APPEND(event, manager->events, event); - log_device_debug(dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) is queued", seqnum, action); + log_device_debug(dev, "Device (SEQNUM=%"PRIu64", ACTION=%s) is queued", + seqnum, device_action_to_string(action)); return 0; } @@ -727,7 +715,7 @@ static int is_device_busy(Manager *manager, struct event *event) { if (sd_device_get_devnum(loop_event->dev, &d) >= 0 && devnum == d && is_block == streq(s, "block")) - return true; + goto set_delaying_seqnum; } /* check network device ifindex */ @@ -736,17 +724,15 @@ static int is_device_busy(Manager *manager, struct event *event) { if (sd_device_get_ifindex(loop_event->dev, &i) >= 0 && ifindex == i) - return true; + goto set_delaying_seqnum; } if (sd_device_get_devpath(loop_event->dev, &loop_devpath) < 0) continue; /* check our old name */ - if (devpath_old && streq(devpath_old, loop_devpath)) { - event->delaying_seqnum = loop_event->seqnum; - return true; - } + if (devpath_old && streq(devpath_old, loop_devpath)) + goto set_delaying_seqnum; loop_devpath_len = strlen(loop_devpath); @@ -758,28 +744,23 @@ static int is_device_busy(Manager *manager, struct event *event) { continue; /* identical device event found */ - if (devpath_len == loop_devpath_len) { - /* devices names might have changed/swapped in the meantime */ - if (major(devnum) != 0 || ifindex > 0) - continue; - event->delaying_seqnum = loop_event->seqnum; - return true; - } + if (devpath_len == loop_devpath_len) + goto set_delaying_seqnum; /* parent device event found */ - if (devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } + if (devpath[common] == '/') + goto set_delaying_seqnum; /* child device event found */ - if (loop_devpath[common] == '/') { - event->delaying_seqnum = loop_event->seqnum; - return true; - } + if (loop_devpath[common] == '/') + goto set_delaying_seqnum; } return false; + +set_delaying_seqnum: + event->delaying_seqnum = loop_event->seqnum; + return true; } static int on_exit_timeout(sd_event_source *s, uint64_t usec, void *userdata) { @@ -806,13 +787,11 @@ static void manager_exit(Manager *manager) { "STATUS=Starting shutdown..."); /* close sources of new events and discard buffered events */ - manager->ctrl_event = sd_event_source_unref(manager->ctrl_event); manager->ctrl = udev_ctrl_unref(manager->ctrl); manager->inotify_event = sd_event_source_unref(manager->inotify_event); manager->fd_inotify = safe_close(manager->fd_inotify); - manager->uevent_event = sd_event_source_unref(manager->uevent_event); manager->monitor = sd_device_monitor_unref(manager->monitor); /* discard queued events and kill workers */ @@ -1007,59 +986,44 @@ static int on_uevent(sd_device_monitor *monitor, sd_device *dev, void *userdata) } /* receive the udevd message from userspace */ -static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userdata) { +static int on_ctrl_msg(struct udev_ctrl *uctrl, enum udev_ctrl_msg_type type, const union udev_ctrl_msg_value *value, void *userdata) { Manager *manager = userdata; - _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, r; + int r; + assert(value); assert(manager); - ctrl_conn = udev_ctrl_get_connection(manager->ctrl); - if (!ctrl_conn) - return 1; - - ctrl_msg = udev_ctrl_receive_msg(ctrl_conn); - if (!ctrl_msg) - return 1; - - i = udev_ctrl_get_set_log_level(ctrl_msg); - if (i >= 0) { - log_debug("Received udev control message (SET_LOG_LEVEL), setting log_priority=%i", i); - log_set_max_level_realm(LOG_REALM_UDEV, i); - log_set_max_level_realm(LOG_REALM_SYSTEMD, i); + switch (type) { + case UDEV_CTRL_SET_LOG_LEVEL: + log_debug("Received udev control message (SET_LOG_LEVEL), setting log_priority=%i", value->intval); + log_set_max_level_realm(LOG_REALM_UDEV, value->intval); + log_set_max_level_realm(LOG_REALM_SYSTEMD, value->intval); manager_kill_workers(manager); - } - - if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { + break; + case UDEV_CTRL_STOP_EXEC_QUEUE: log_debug("Received udev control message (STOP_EXEC_QUEUE)"); manager->stop_exec_queue = true; - } - - if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { + break; + case UDEV_CTRL_START_EXEC_QUEUE: 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) { + break; + case UDEV_CTRL_RELOAD: log_debug("Received udev control message (RELOAD)"); manager_reload(manager); - } - - str = udev_ctrl_get_set_env(ctrl_msg); - if (str) { + break; + case UDEV_CTRL_SET_ENV: { _cleanup_free_ char *key = NULL, *val = NULL, *old_key = NULL, *old_val = NULL; - char *eq; + const char *eq; - eq = strchr(str, '='); + eq = strchr(value->buf, '='); if (!eq) { - log_error("Invalid key format '%s'", str); + log_error("Invalid key format '%s'", value->buf); return 1; } - key = strndup(str, eq - str); + key = strndup(value->buf, eq - value->buf); if (!key) { log_oom(); return 1; @@ -1100,27 +1064,30 @@ static int on_ctrl_msg(sd_event_source *s, int fd, uint32_t revents, void *userd key = val = NULL; manager_kill_workers(manager); + break; } + case UDEV_CTRL_SET_CHILDREN_MAX: + if (value->intval <= 0) { + log_debug("Received invalid udev control message (SET_MAX_CHILDREN, %i), ignoring.", value->intval); + return 0; + } - i = udev_ctrl_get_set_children_max(ctrl_msg); - if (i >= 0) { - log_debug("Receivd udev control message (SET_MAX_CHILDREN), setting children_max=%i", i); - arg_children_max = i; + log_debug("Received udev control message (SET_MAX_CHILDREN), setting children_max=%i", value->intval); + arg_children_max = value->intval; (void) sd_notifyf(false, "READY=1\n" "STATUS=Processing with %u children at max", arg_children_max); - } - - if (udev_ctrl_get_ping(ctrl_msg) > 0) + break; + case UDEV_CTRL_PING: log_debug("Received udev control message (PING)"); - - if (udev_ctrl_get_exit(ctrl_msg) > 0) { + break; + case UDEV_CTRL_EXIT: 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 */ - manager->ctrl_conn_blocking = udev_ctrl_connection_ref(ctrl_conn); + break; + default: + log_debug("Received unknown udev control message, ignoring"); } return 1; @@ -1603,9 +1570,9 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg .cgroup = cgroup, }; - 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"); + r = udev_ctrl_new_from_fd(&manager->ctrl, fd_ctrl); + if (r < 0) + return log_error_errno(r, "Failed to initialize udev control socket: %m"); r = udev_ctrl_enable_receiving(manager->ctrl); if (r < 0) @@ -1627,7 +1594,9 @@ static int manager_new(Manager **ret, int fd_ctrl, int fd_uevent, const char *cg } static int main_loop(Manager *manager) { - int fd_worker, fd_ctrl, r; + int fd_worker, r; + + manager->pid = getpid_cached(); /* unnamed socket from workers to the main daemon */ r = socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, manager->worker_watch); @@ -1674,19 +1643,19 @@ static int main_loop(Manager *manager) { if (r < 0) return log_error_errno(r, "Failed to create watchdog event source: %m"); - 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"); - - r = sd_event_add_io(manager->event, &manager->ctrl_event, fd_ctrl, EPOLLIN, on_ctrl_msg, manager); + r = udev_ctrl_attach_event(manager->ctrl, manager->event); if (r < 0) - return log_error_errno(r, "Failed to create udev control event source: %m"); + return log_error_errno(r, "Failed to attach event to udev control: %m"); + + r = udev_ctrl_start(manager->ctrl, on_ctrl_msg, manager); + if (r < 0) + return log_error_errno(r, "Failed to start device monitor: %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 * (including the synthetic ones we may create due to inotify events). */ - r = sd_event_source_set_priority(manager->ctrl_event, SD_EVENT_PRIORITY_IDLE); + r = sd_event_source_set_priority(udev_ctrl_get_event_source(manager->ctrl), SD_EVENT_PRIORITY_IDLE); if (r < 0) return log_error_errno(r, "Failed to set IDLE event priority for udev control event source: %m"); @@ -1846,10 +1815,7 @@ static int run(int argc, char *argv[]) { log_debug_errno(r, "Failed to adjust OOM score, ignoring: %m"); } - r = main_loop(manager); - /* FIXME: move this into manager_free() */ - udev_ctrl_cleanup(manager->ctrl); - return r; + return main_loop(manager); } DEFINE_MAIN_FUNCTION(run); diff --git a/src/udev/v4l_id/v4l_id.c b/src/udev/v4l_id/v4l_id.c index 4d482891a..4d60ee3c8 100644 --- a/src/udev/v4l_id/v4l_id.c +++ b/src/udev/v4l_id/v4l_id.c @@ -64,22 +64,27 @@ int main(int argc, char *argv[]) { return 3; if (ioctl(fd, VIDIOC_QUERYCAP, &v2cap) == 0) { + int capabilities; printf("ID_V4L_VERSION=2\n"); printf("ID_V4L_PRODUCT=%s\n", v2cap.card); printf("ID_V4L_CAPABILITIES=:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0 || - (v2cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) > 0) + if (v2cap.capabilities & V4L2_CAP_DEVICE_CAPS) + capabilities = v2cap.device_caps; + else + capabilities = v2cap.capabilities; + if ((capabilities & V4L2_CAP_VIDEO_CAPTURE) > 0 || + (capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) > 0) printf("capture:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0 || - (v2cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) > 0) + if ((capabilities & V4L2_CAP_VIDEO_OUTPUT) > 0 || + (capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) > 0) printf("video_output:"); - if ((v2cap.capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) + if ((capabilities & V4L2_CAP_VIDEO_OVERLAY) > 0) printf("video_overlay:"); - if ((v2cap.capabilities & V4L2_CAP_AUDIO) > 0) + if ((capabilities & V4L2_CAP_AUDIO) > 0) printf("audio:"); - if ((v2cap.capabilities & V4L2_CAP_TUNER) > 0) + if ((capabilities & V4L2_CAP_TUNER) > 0) printf("tuner:"); - if ((v2cap.capabilities & V4L2_CAP_RADIO) > 0) + if ((capabilities & V4L2_CAP_RADIO) > 0) printf("radio:"); printf("\n"); } diff --git a/src/update-done/update-done.c b/src/update-done/update-done.c index c76c2d1f5..bbd14165d 100644 --- a/src/update-done/update-done.c +++ b/src/update-done/update-done.c @@ -1,9 +1,13 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ +#include +#include +#include + #include "alloc-util.h" #include "fileio-label.h" #include "selinux-util.h" -#include "util.h" +#include "time-util.h" #define MESSAGE \ "# This file was created by systemd-update-done. Its only \n" \ diff --git a/src/update-utmp/update-utmp.c b/src/update-utmp/update-utmp.c index 7d66ed335..bfc78b973 100644 --- a/src/update-utmp/update-utmp.c +++ b/src/update-utmp/update-utmp.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include #if HAVE_AUDIT diff --git a/src/user-sessions/user-sessions.c b/src/user-sessions/user-sessions.c index 490d9f01a..c24142951 100644 --- a/src/user-sessions/user-sessions.c +++ b/src/user-sessions/user-sessions.c @@ -1,6 +1,9 @@ /* SPDX-License-Identifier: LGPL-2.1+ */ #include +#include +#include +#include #include #include "fileio.h" @@ -10,7 +13,6 @@ #include "log.h" #include "selinux-util.h" #include "string-util.h" -#include "util.h" static int run(int argc, char *argv[]) { int r, k; diff --git a/src/vconsole/vconsole-setup.c b/src/vconsole/vconsole-setup.c index ebdeba3e8..67dc2e49f 100644 --- a/src/vconsole/vconsole-setup.c +++ b/src/vconsole/vconsole-setup.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include #include "alloc-util.h" diff --git a/src/volatile-root/volatile-root.c b/src/volatile-root/volatile-root.c index 5da9ce168..d1193a7a2 100644 --- a/src/volatile-root/volatile-root.c +++ b/src/volatile-root/volatile-root.c @@ -3,6 +3,8 @@ #include #include "alloc-util.h" +#include "blockdev-util.h" +#include "escape.h" #include "fs-util.h" #include "main-func.h" #include "mkdir.h" @@ -17,20 +19,7 @@ static int make_volatile(const char *path) { _cleanup_free_ char *old_usr = NULL; int r; - 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) - return log_error_errno(SYNTHETIC_ERRNO(EINVAL), - "%s is not a mount point.", path); - - r = path_is_temporary_fs(path); - if (r < 0) - return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path); - if (r > 0) { - log_info("%s already is a temporary file system.", path); - return 0; - } + assert(path); r = chase_symlinks("/usr", path, CHASE_PREFIX_ROOT, &old_usr); if (r < 0) @@ -45,7 +34,7 @@ static int make_volatile(const char *path) { goto finish_rmdir; if (mkdir("/run/systemd/volatile-sysroot/usr", 0755) < 0) { - r = -errno; + r = log_error_errno(errno, "Failed to create /usr directory: %m"); goto finish_umount; } @@ -53,9 +42,11 @@ static int make_volatile(const char *path) { if (r < 0) goto finish_umount; - r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", true, NULL); - if (r < 0) + r = bind_remount_recursive("/run/systemd/volatile-sysroot/usr", MS_RDONLY, MS_RDONLY, NULL); + if (r < 0) { + log_error_errno(r, "Failed to remount /usr read-only: %m"); goto finish_umount; + } r = umount_recursive(path, 0); if (r < 0) { @@ -64,7 +55,7 @@ static int make_volatile(const char *path) { } if (mount(NULL, "/", NULL, MS_SLAVE|MS_REC, NULL) < 0) - log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC: %m", path); + log_warning_errno(errno, "Failed to remount %s MS_SLAVE|MS_REC, ignoring: %m", path); r = mount_verbose(LOG_ERR, "/run/systemd/volatile-sysroot", path, NULL, MS_MOVE, NULL); @@ -77,9 +68,55 @@ finish_rmdir: return r; } +static int make_overlay(const char *path) { + _cleanup_free_ char *escaped_path = NULL; + bool tmpfs_mounted = false; + const char *options = NULL; + int r; + + assert(path); + + r = mkdir_p("/run/systemd/overlay-sysroot", 0700); + if (r < 0) + return log_error_errno(r, "Couldn't create overlay sysroot directory: %m"); + + r = mount_verbose(LOG_ERR, "tmpfs", "/run/systemd/overlay-sysroot", "tmpfs", MS_STRICTATIME, "mode=755"); + if (r < 0) + goto finish; + + tmpfs_mounted = true; + + if (mkdir("/run/systemd/overlay-sysroot/upper", 0755) < 0) { + r = log_error_errno(errno, "Failed to create /run/systemd/overlay-sysroot/upper: %m"); + goto finish; + } + + if (mkdir("/run/systemd/overlay-sysroot/work", 0755) < 0) { + r = log_error_errno(errno, "Failed to create /run/systemd/overlay-sysroot/work: %m"); + goto finish; + } + + escaped_path = shell_escape(path, ",:"); + if (!escaped_path) { + r = log_oom(); + goto finish; + } + + options = strjoina("lowerdir=", escaped_path, ",upperdir=/run/systemd/overlay-sysroot/upper,workdir=/run/systemd/overlay-sysroot/work"); + r = mount_verbose(LOG_ERR, "overlay", path, "overlay", 0, options); + +finish: + if (tmpfs_mounted) + (void) umount_verbose("/run/systemd/overlay-sysroot"); + + (void) rmdir("/run/systemd/overlay-sysroot"); + return r; +} + static int run(int argc, char *argv[]) { VolatileMode m = _VOLATILE_MODE_INVALID; const char *path; + dev_t devt; int r; log_setup_service(); @@ -94,10 +131,8 @@ static int run(int argc, char *argv[]) { 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; - } + if (m < 0) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Couldn't parse volatile mode: %s", argv[1]); } if (argc < 3) @@ -116,10 +151,47 @@ static int run(int argc, char *argv[]) { "Directory cannot be the root directory."); } - if (m != VOLATILE_YES) + if (!IN_SET(m, VOLATILE_YES, VOLATILE_OVERLAY)) return 0; - return make_volatile(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) + return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "%s is not a mount point.", path); + + r = path_is_temporary_fs(path); + if (r < 0) + return log_error_errno(r, "Couldn't determine whether %s is a temporary file system: %m", path); + if (r > 0) { + log_info("%s already is a temporary file system.", path); + return 0; + } + + /* We are about to replace the root directory with something else. Later code might want to know what we + * replaced here, hence let's save that information as a symlink we can later use. (This is particularly + * relevant for the overlayfs case where we'll fully obstruct the view onto the underlying device, hence + * querying the backing device node from the file system directly is no longer possible. */ + r = get_block_device_harder(path, &devt); + if (r < 0) + return log_error_errno(r, "Failed to determine device major/minor of %s: %m", path); + else if (r > 0) { + _cleanup_free_ char *dn = NULL; + + r = device_path_make_major_minor(S_IFBLK, devt, &dn); + if (r < 0) + return log_error_errno(r, "Failed to format device node path: %m"); + + if (symlink(dn, "/run/systemd/volatile-root") < 0) + log_warning_errno(errno, "Failed to create symlink /run/systemd/volatile-root: %m"); + } + + if (m == VOLATILE_YES) + return make_volatile(path); + else { + assert(m == VOLATILE_OVERLAY); + return make_overlay(path); + } } DEFINE_MAIN_FUNCTION(run); diff --git a/test/README.testsuite b/test/README.testsuite index 2edcf6a70..7ff148e13 100644 --- a/test/README.testsuite +++ b/test/README.testsuite @@ -44,11 +44,38 @@ 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_QEMU=1 + Don't run tests under QEMU -TEST_NO_NSPAWN=1 can be used to disable nspawn tests. +TEST_NO_NSPAWN=1 + Don't run tests under systemd-nspawn -KERNEL_APPEND='...' can be used to add additional kernel parameters for the QEMU runs. +TEST_NO_KVM=1 + Disable QEMU KVM autodetection (may be necessary when you're trying to run the + *vanilla* QEMU and have both qemu and qemu-kvm installed) + +QEMU_MEM=512M + Configure amount of memory for QEMU VMs (defaults to 512M) + +QEMU_SMP=1 + Configure number of CPUs for QEMU VMs (defaults to 1) + +KERNEL_APPEND='...' + Append additional parameters to the kernel command line + +NSPAWN_ARGUMENTS='...' + Specify additional arguments for systemd-nspawn + +QEMU_TIMEOUT=infinity + Set a timeout for tests under QEMU (defaults to infinity) + +NSPAWN_TIMEOUT=infinity + Set a timeout for tests under systemd-nspawn (defaults to infinity) + +INTERACTIVE_DEBUG=1 + Configure the machine to be more *user-friendly* for interactive debuggung + (e.g. by setting a usable default terminal, suppressing the shutdown after + the test, etc.) 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) diff --git a/test/TEST-02-CRYPTSETUP/test.sh b/test/TEST-02-CRYPTSETUP/test.sh index 71709f7d5..97eb2f409 100755 --- a/test/TEST-02-CRYPTSETUP/test.sh +++ b/test/TEST-02-CRYPTSETUP/test.sh @@ -29,7 +29,7 @@ check_result_qemu() { test_setup() { create_empty_image echo -n test >$TESTDIR/keyfile - cryptsetup -q luksFormat ${LOOPDEV}p2 $TESTDIR/keyfile + cryptsetup -q luksFormat --pbkdf pbkdf2 --pbkdf-force-iterations 1000 ${LOOPDEV}p2 $TESTDIR/keyfile cryptsetup luksOpen ${LOOPDEV}p2 varcrypt <$TESTDIR/keyfile mkfs.ext4 -L var /dev/mapper/varcrypt mkdir -p $TESTDIR/root diff --git a/test/TEST-03-JOBS/test-jobs.sh b/test/TEST-03-JOBS/test-jobs.sh index e66ea5362..42190cf47 100755 --- a/test/TEST-03-JOBS/test-jobs.sh +++ b/test/TEST-03-JOBS/test-jobs.sh @@ -26,6 +26,13 @@ grep 'sleep\.service.*running' /root/list-jobs.txt grep 'hello\.service' /root/list-jobs.txt && exit 1 systemctl stop sleep.service hello-after-sleep.target +# Some basic testing that --show-transaction does something useful +! systemctl is-active systemd-importd +systemctl -T start systemd-importd +systemctl is-active systemd-importd +systemctl --show-transaction stop systemd-importd +! systemctl is-active systemd-importd + # Test for a crash when enqueuing a JOB_NOP when other job already exists systemctl start --no-block hello-after-sleep.target # hello.service should still be waiting, so these try-restarts will collapse diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh index 260cae09a..e198cb6ed 100755 --- a/test/TEST-04-JOURNAL/test-journal.sh +++ b/test/TEST-04-JOURNAL/test-journal.sh @@ -63,6 +63,18 @@ grep -q '^PRIORITY=6$' /output ! grep -q '^FOO=' /output ! grep -q '^SYSLOG_FACILITY=' /output +# `-b all` negates earlier use of -b (-b and -m are otherwise exclusive) +journalctl -b -1 -b all -m > /dev/null + +# -b always behaves like -b0 +journalctl -q -b-1 -b0 | head -1 > /expected +journalctl -q -b-1 -b | head -1 > /output +cmp /expected /output +# ... even when another option follows (both of these should fail due to -m) +{ journalctl -ball -b0 -m 2>&1 || :; } | head -1 > /expected +{ journalctl -ball -b -m 2>&1 || :; } | head -1 > /output +cmp /expected /output + # Don't lose streams on restart systemctl start forever-print-hola sleep 3 diff --git a/test/TEST-11-ISSUE-3166/test.sh b/test/TEST-11-ISSUE-3166/test.sh index 6cde7fd7e..38c725fa7 100755 --- a/test/TEST-11-ISSUE-3166/test.sh +++ b/test/TEST-11-ISSUE-3166/test.sh @@ -41,6 +41,8 @@ EOF cat >$initdir/etc/systemd/system/fail-on-restart.service < /testok diff --git a/test/TEST-22-TMPFILES/test-08.sh b/test/TEST-22-TMPFILES/test-08.sh new file mode 100755 index 000000000..e7bf04478 --- /dev/null +++ b/test/TEST-22-TMPFILES/test-08.sh @@ -0,0 +1,32 @@ +#! /bin/bash +# +# Verify tmpfiles can run in a root directory under a path prefix that contains +# directories owned by unprivileged users, for example when a root file system +# is mounted in a regular user's home directory. +# +# https://github.com/systemd/systemd/pull/11820 +# + +set -e + +rm -fr /tmp/root /tmp/user +mkdir -p /tmp/root /tmp/user/root +chown daemon:daemon /tmp/user + +# Verify the command works as expected with no prefix or a root-owned prefix. +echo 'd /tmp/root/test1' | systemd-tmpfiles --create - +test -d /tmp/root/test1 +echo 'd /test2' | systemd-tmpfiles --root=/tmp/root --create - +test -d /tmp/root/test2 + +# Verify the command fails to write to a root-owned subdirectory under an +# unprivileged user's directory when it's not part of the prefix, as expected +# by the unsafe_transition function. +! echo 'd /tmp/user/root/test' | systemd-tmpfiles --create - +! test -e /tmp/user/root/test +! echo 'd /user/root/test' | systemd-tmpfiles --root=/tmp --create - +! test -e /tmp/user/root/test + +# Verify the above works when all user-owned directories are in the prefix. +echo 'd /test' | systemd-tmpfiles --root=/tmp/user/root --create - +test -d /tmp/user/root/test diff --git a/test/TEST-23-TYPE-EXEC/testsuite.sh b/test/TEST-23-TYPE-EXEC/testsuite.sh index 80734bbbd..4f7cc4431 100755 --- a/test/TEST-23-TYPE-EXEC/testsuite.sh +++ b/test/TEST-23-TYPE-EXEC/testsuite.sh @@ -4,8 +4,8 @@ set -ex set -o pipefail -systemd-analyze set-log-level debug -systemd-analyze set-log-target console +systemd-analyze log-level debug +systemd-analyze log-target console # Create a binary for which execve() will fail touch /tmp/brokenbinary @@ -21,7 +21,7 @@ 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 +systemd-analyze log-level info echo OK > /testok diff --git a/test/TEST-24-UNIT-TESTS/test.sh b/test/TEST-24-UNIT-TESTS/test.sh index 014ee5227..fc8c89fe0 100755 --- a/test/TEST-24-UNIT-TESTS/test.sh +++ b/test/TEST-24-UNIT-TESTS/test.sh @@ -78,6 +78,8 @@ test_setup() { setup_basic_environment install_keymaps yes install_zoneinfo + # Install nproc to determine # of CPUs for correct parallelization + inst_binary nproc # setup the testsuite service cat >$initdir/etc/systemd/system/testsuite.service < /$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 +NPROC=$(nproc) +MAX_QUEUE_SIZE=${NPROC:-2} +IFS=$'\n' TEST_LIST=($(ls /usr/lib/systemd/tests/test-*)) + +# Check & report test results +# Arguments: +# $1: test path +# $2: test exit code +function report_result() { + if [[ $# -ne 2 ]]; then + echo >&2 "check_result: missing arguments" + exit 1 fi - systemd-cat echo "--- $NAME ---" - systemd-cat cat /$NAME.log + local name="${1##*/}" + local ret=$2 + + if [[ $ret -ne 0 && $ret != 77 ]]; then + echo "$name failed with $ret" + echo "$name" >> /failed-tests + { + echo "--- $name begin ---" + cat "/$name.log" + echo "--- $name end ---" + } >> /failed + elif [[ $ret == 77 ]]; then + echo "$name skipped" + echo "$name" >> /skipped-tests + { + echo "--- $name begin ---" + cat "/$name.log" + echo "--- $name end ---" + } >> /skipped + else + echo "$name OK" + echo "$name" >> /testok + fi + + systemd-cat echo "--- $name ---" + systemd-cat cat "/$name.log" +} + +# Associative array for running tasks, where running[test-path]=PID +declare -A running=() +for task in "${TEST_LIST[@]}"; do + # If there's MAX_QUEUE_SIZE running tasks, keep checking the running queue + # until one of the tasks finishes, so we can replace it. + while [[ ${#running[@]} -ge $MAX_QUEUE_SIZE ]]; do + for key in "${!running[@]}"; do + if ! kill -0 ${running[$key]} &>/dev/null; then + # Task has finished, report its result and drop it from the queue + wait ${running[$key]} + ec=$? + report_result "$key" $ec + unset running["$key"] + # Break from inner for loop and outer while loop to skip + # the sleep below when we find a free slot in the queue + break 2 + fi + done + + # Precisely* calculated constant to keep the spinlock from burning the CPU(s) + sleep 0.01 + done + + if [[ -x $task ]]; then + log_file="/${task##*/}.log" + $task &> "$log_file" & + running[$task]=$! + fi +done + +# Wait for remaining running tasks +for key in "${!running[@]}"; do + wait ${running[$key]} + ec=$? + report_result "$key" $ec + unset running["$key"] done exit 0 diff --git a/test/TEST-27-STDOUTFILE/testsuite.sh b/test/TEST-27-STDOUTFILE/testsuite.sh index b8fcc46e3..6975b7966 100755 --- a/test/TEST-27-STDOUTFILE/testsuite.sh +++ b/test/TEST-27-STDOUTFILE/testsuite.sh @@ -4,8 +4,8 @@ set -ex set -o pipefail -systemd-analyze set-log-level debug -systemd-analyze set-log-target console +systemd-analyze log-level debug +systemd-analyze log-target console systemd-run --wait --unit=one -p StandardOutput=file:/tmp/stdout -p StandardError=file:/tmp/stderr -p Type=exec sh -c 'echo x ; echo y >&2' cmp /tmp/stdout < /testok diff --git a/test/TEST-29-UDEV-ID_RENAMING/Makefile b/test/TEST-29-UDEV-ID_RENAMING/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-29-UDEV-ID_RENAMING/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-29-UDEV-ID_RENAMING/test.sh b/test/TEST-29-UDEV-ID_RENAMING/test.sh new file mode 100755 index 000000000..74362c052 --- /dev/null +++ b/test/TEST-29-UDEV-ID_RENAMING/test.sh @@ -0,0 +1,49 @@ +#!/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="UDEV ID_RENAMING property" +TEST_NO_NSPAWN=1 + +. $TEST_BASE_DIR/test-functions +QEMU_TIMEOUT=300 + +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 -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 + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service < /run/udev/rules.d/50-testsuite.rules < /testok + +exit 0 diff --git a/test/TEST-30-ONCLOCKCHANGE/Makefile b/test/TEST-30-ONCLOCKCHANGE/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-30-ONCLOCKCHANGE/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-30-ONCLOCKCHANGE/test.sh b/test/TEST-30-ONCLOCKCHANGE/test.sh new file mode 100755 index 000000000..a9b3d78d6 --- /dev/null +++ b/test/TEST-30-ONCLOCKCHANGE/test.sh @@ -0,0 +1,60 @@ +#!/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 OnClockChange= + OnTimezoneChange=" +TEST_NO_NSPAWN=1 + +. $TEST_BASE_DIR/test-functions + +test_setup() { + create_empty_image + mkdir -p $TESTDIR/root + mount ${LOOPDEV}p1 $TESTDIR/root + + ( + LOG_LEVEL=5 + eval $(udevadm info --export --query=env --name=${LOOPDEV}p2) + + inst_any /usr/share/zoneinfo/Europe/Kiev + inst_any /usr/share/zoneinfo/Europe/Berlin + + 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 + + # extend the watchdog + mkdir -p $initdir/etc/systemd/system/systemd-timedated.service.d + cat >$initdir/etc/systemd/system/systemd-timedated.service.d/watchdog.conf <$initdir/etc/systemd/system/testsuite.service < /testok + +exit 0 diff --git a/test/TEST-31-DEVICE-ENUMERATION/Makefile b/test/TEST-31-DEVICE-ENUMERATION/Makefile new file mode 120000 index 000000000..e9f93b110 --- /dev/null +++ b/test/TEST-31-DEVICE-ENUMERATION/Makefile @@ -0,0 +1 @@ +../TEST-01-BASIC/Makefile \ No newline at end of file diff --git a/test/TEST-31-DEVICE-ENUMERATION/test.sh b/test/TEST-31-DEVICE-ENUMERATION/test.sh new file mode 100755 index 000000000..d7cea7336 --- /dev/null +++ b/test/TEST-31-DEVICE-ENUMERATION/test.sh @@ -0,0 +1,49 @@ +#!/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="plugged -> dead -> plugged issue #11997" +TEST_NO_NSPAWN=1 + +. $TEST_BASE_DIR/test-functions +QEMU_TIMEOUT=300 + +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 -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 + + # setup the testsuite service + cat >$initdir/etc/systemd/system/testsuite.service < dead'; then + exit 1 +fi + +echo OK > /testok +exit 0 diff --git a/test/fuzz/fuzz-bus-message/oss-fuzz-14016 b/test/fuzz/fuzz-bus-message/oss-fuzz-14016 new file mode 100644 index 0000000000000000000000000000000000000000..c82d1ba4adfbc829ef148bfc61907c51f1fc9b74 GIT binary patch literal 49343 zcmeHPOO7PR4Q&W8prww{S{h#XJZcQQGN6UMkTyPqbX6U|3^JK~43d!%oz>k{HQ9?u z^6~K#WaMXhM*8l{ET!ef^gI z{MvrL@v4gu-zI-7EI1GdS`^tS!nZ;C)Vi z2wD``DZ;lw`qVJ4Qj}dJ`Ab8>HAuX0HCrbcsWXsjwlBVIwuA2~EPFX)ke#`Bs|tr0 zI6?DRq_WCq0uho(IhqhV$iIcQcDQLKwas)*IO5#pY-(nJOpo~Ftc7MWV8Xk-_4>nQ?wReT%iRiLsHmE z6<+6Z;#{sw(vPueqb}>1yqBWsm8nos;tJMvF^QMfF#tKlkzMleI_eM+l1Mq45IZOx z_Tgh^x{0!&k2XCA}MU8?B#oySn3AX90fulULSQZX%(TFGLxct8B8(6M}zG^#@?RVz&$Ja zf}2JW%fqBKc`rrbDg-D_fUts#7mW!xfuKc^og#c2q)!c-D@EBwlD{-0T!X|5SF?4J zkvapZX1hM>Vs~hm(jb_s0Fm1ewuxy(Q|A*ObsB{T{i-pjDhG+L(_ecAvI`Ja-B!37 z6HEkx7DaZ7@NJMjHEgaFWfw{Q(vWZs5-(iM)=5U{45XUv`a?_X4h>Tp1XC3tavQ=n zF^y>Ie0^iIE@?ORtF&%g$DIf3x+od;w5iY@f#Jv9Hm+Jy0(%6MC1eK3^oTDxE+3ka zY!R&MViGSM=>X&qM|R1>D-~J3ABm7e%F%?_LFuu&&f(r-8A{i*kxHWzA2?{EAZ!Ef zS<&Ze8bvG*lkUWODGFC1Kyd9X%G#48QJiK2Mn8aW$cq)0Iv4g1;uEGV0eO(+pM|KbDp&L{}mB-)6k`A&S&Y6yO- znUY9QQ(`>{T{i-pjDhG+L(_C%ZG+ur2EllJ{zsYiV%u)lw~7gx&m)0==ImD4&^6*MEm3JT!l1Mq45Id;d?As4#?#zcO`LZ9x z*?`;?l89eUQ}o1Zh<7D%R*Tf^$Jc%oTPuf`a^Iz)tfTZO4P~fi5HDOyx{)HqY}bcd z?9S?81IM$k{t)c=vu6f4e)pd5?@UBf@cseCKE4eMQD?R>&7WMO#_$%-Y1N zSfDz}(a9Lv-*;?Weh%Rfuk<^ldOrUf!x6H?i`%&@0uf6RIIUn=MU;7w0- za+cy7+cfel50k5+ztz>)t`ZiC5Fo7R<78t3Odx1cWTyz<2I*77=1Ng^k>oE83D+R; z!qse@WTehOs@cAS8%H)9n@}P&O!0xKj)JgFqK#;p@5D!)hTsXBl1NZfVm%36Hv*iD zf#^>|(};Zj(V9%}0VXo&Q)lvRgWYBZ!FavC0lG#`tG=;KBiHi>Rl-6Q0)!Pt>_uY& zP9SJeWTyz<2I*77=1Ng^k>oE83D+R;!qse@WTehOs@bk@fY==xrZfnqDnR5mgl%FP z(bW0GH-Hp0OfocT33z?L-NIbf#pMQZv+lf0GSJl`@faM)?Uiu-rz2^TmJihGm^gu? zp2_tQ&zJSv3_NCvj-9>pE=j?wMcOu@KSR+suW3ZTKF_R?yL5e?4G+?8{Gb!H&tVfN zpJdt-E8v;F9B)gt*}&?|2}GI6^%0LxEk(b)P46V=m5RRRrjh0R1E5QMC-3&weBQ8> z=l9;1Ec%zuE0*+tr&CYbyRI`Mid}LD4BX+WND5m==bu>W2G<;A!r9{^SyrbGwH=Aq zmv=hS);2j3M-2q~>Xuy777!Rvur)v$&>x`X zw%kicbUS?2A@9Yd9nc;W#thyAPG;b0B+yJ}ue;h9c6YL3ha6>^^cXkQ8V-1 z$xT=t({;z`qiNLYPwtXTbaj;O91NBI!&X}`x|Z!J4dW8T4c3xw9H0;gS`^tS!nZ;C z)T6$sO?HvwFAWLTAo0T0Y@KAJ&OoZ!p7O8nV0$9Q4s3`qfkGf?QDmnG-v;SZ>p>b_ z%T1PsVXfkYYe_d!q?qj~51HSvY}R8#jpivnFxWCk`{HGzge-ZG@gqKxE&NOc#wKZL zyTreP-NInj#pMQZx5SHAN?HDnmI$u7qU2~o?4b7R;PB(#5m1)a792`IoV%P&%?yx< z?vJPXzkapbJ#?Y$8W^%ZaiG!=(jWjW-bA!I*{A>$2wD``DZ;lw`qa>-Qj}dJ`Ab8> zHAuX0HCrbcsWXsjw(IQ}yFx^;0h?5tx6c69J0g{@TN=ieRH8TXxHRf%M@ z2}xY4j&x@WGEM$5;IPm#Kb6EbDQ!g4;;*lV@q3MWsI7u)jsl?&(^vH2xF=4eP|AU4 z2EllpJeSjT5YcCB8iD2AukG?)il$elLPd!ySl7iQURuWhJ4zocKW8Q4qEP_pIo1HH{*kzuhD(6emDf_dfK3F~LG0Xi;RR z2;T{bq#KRcXaqucBV88ixe+hOS+LF z#cU6JBha=UU?PVnWGBc_;;^<~$$P_7OpvxN``CM+fZwcYOa|_;NFL64*Az{!eI6;& zQIAs(7)d-Ad_v(61D>R?m8vN}A&Hel%F%?_LG3$g`{DGR`A{WaDO;oLK<)}j#4o2Q zdg3+21xcK}rpY2T`|-6O1tciNa^Iz)tfTZO4P~fi5HDOyx{)HqY!CdtnmseJ+1P{< znGLJ~IDsfLxz64(+wH8`yX#B^&gB7jTi&=chUErvx9+@4GSSr{-8mR4U;GqpwaMIY zEh{#Xqs*L+lJmY}$%W1B{vfE|UDE zA>kS%UbvdAlZ@0ENHyDcaO22kV-re*hABQU)lm?(Nwg77^PTvp(-1sCQxXYkN~|ZL z>qdZ+F%bP}Xd02vKU$ONJ-|c;edccdtL8!+}~C zXGRoH-MyTNSXn^eI0jn-qyhcizucC4>4Ocitr# z>}rwOaVW6sz0^Ne_C_rqsMRrX0!cTM>m%O6>$e$r%oM$Ma~D3iOZ&@0s%z_>x)%xx zY;j$bn0=-tokI*ftZb2}$lHBM5AxEBIO~VKpZmf{&m2lj{F4eY10uE#53W<-tiaEm5KPu^wmAQ25(s;|sW(G*~ z{Et8#3U7L;r(?2jY}1H;eLHBo;TmtCac7&=cNc@P1%OJt)j<0AdvUOdvRZP6dPlVI o7rZ68H1w4^UA%C0W>k!XsK{n(((kXYZ*RZ-{QCBD`}yhR|MEbWjsO4v literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-calendarspec/oss-fuzz-14108 b/test/fuzz/fuzz-calendarspec/oss-fuzz-14108 new file mode 100644 index 000000000..6899c23a7 --- /dev/null +++ b/test/fuzz/fuzz-calendarspec/oss-fuzz-14108 @@ -0,0 +1 @@ +@67767992554749550 \ No newline at end of file diff --git a/test/fuzz/fuzz-dns-packet/oss-fuzz-13422 b/test/fuzz/fuzz-dns-packet/oss-fuzz-13422 new file mode 100644 index 0000000000000000000000000000000000000000..439aec54e39683d952a81c3adcaccb633c2469df GIT binary patch literal 36510 zcmeI5TaO&Y6~|{C8!Y0NNT4VJM@C7IAkgZrzK$g#*6ZD;C=wDcQKTrdb4e`L*usX8 zmnb~)kgpKsi$wVQlUT4KW)yLhbFY5Q251MRAn$-&Hx9iuhE?2Sn;jYge99ILbvGO^w`H8^S zu$`w}mr%p)z?IJMXa2QW_|KZBjfW5_8(^KsHI5MS!#5w?U$`50Ke~CGjI+Yc_A7Ve zjmgQ$8fx{_to=vL(>LWID2OX0JbZWf;0Xxg4ztuCUs2YUBCwv!TKPqC_B(5?FOtY# zcCM#bb^1_ROZg5@Us136*0A|`V}8%oh9_UA4S!)1-}8Qcc{xGXq-}Wh9r(Tmxc~{+ zfg;~Lq|RNe*DP8+?LfwJ0r}24DCph^6o!1F5&P2z31_d^tt5B1w+paDr;EOHuD&S! zI(<pUyDap!(^Gmnxgk1~FeI@9Wd zD*+rw)98@mX)R7G&! zapcBP<$&&oBTscjax^dFtV-hBz2gCwPZrr?d^}$q2j~pk^v;8+t7iE$$>*W*X1C%+ z<%%j9$HyVK`Q6ET(>wPk5AWQ2F9i4gy$AF6-o5u>S)|i0-1V1M@-n&U4vOsfR(vbT zrne76fMtAp79GYjH=mHo2S(g4<9t5J5B8IBH7x^x)5*ih$)6rPj1Jwx5 z%EHJLnIM_k;&s7zOmAZiVRA&Ggq-22wZJ0E0Qc;68*A1F#u!jb6{;u)ojloM;X5O` zXsqee$dDccEJYR5s4`RmKt>bN24_&K1Sywi3z7$60GR}K7h&=sji8d@J>qR*rn==) z%v@YZUB`&HF(QCVE@lQG(v4ChY=$bh1oGgZ;dPjfPO1tIS!dX!0Eo&sOk@#OS4OJm4mL8(84NQP7lp_Sg2NcZ3|QZzx8xD)l*s0p_7St3LTzG`)WDhu0lZmg z8YVcJ4_t`^#bNTKkQx;ff5KDCE+dUhAMk~8A893-NEkQ`n?00D`Y&X{>Y7Cr!kudY z;WbKhs5f(o&JQoUG$^>Af!UjMZ4cf8;VPvo*;)>Kdtge8t zMizjI1tlCkEtgtjAR`aQI#Gqxn3{P(22<(>{Vi2kDL=ZHhYcxQMhysqi57y$fJgx; zp`)?J0zK6N;ZB%5#1pd1u-p(coNEHJAyOdFlqvw2)-ga*hUF1VV2%)~!t%)SBs_vO z0EM#Dfw&?_N9iE?3i1Zl03s7EH-k9LNd>X-o2$LFjSR0P6w+$Y0XPpEDa3�*X2W zk-_~cgM#IT?U5Oi_9MSxd8{y@GCUAIY;loAMuROTAf_4epta*LXT}f^F7mGoYaxLA zI3*Tg@(^Ll$zbY@D21>mks-y?7+8Zgnx*)7+_$hm*rBkJI3Yv-x83!AUjF(TDydm#G)CgQMHC#iYoytlyde_`!n| zNBoCLS}y!>=Ev8Y{5riAN3(K~-Wt#1%FVhI-+-i`P{esOt7hYJI``1ofILVh`_p4L ziV9)&g_5A&mP|8|;AtISowb)M3^JWOs)W*#3m#%$8|GOn`Rlc7z>#q@YKp3JK< zUBpLTHDwc$BFVkz?&Lu>i|5B4hc+PZPs+5KMsC5lS>m~{22$=pfhjtR9U_wLrF@-| z6Gkel!a=0{xHphWZ%CoN_@Xs?5@U#D|4?&l_9`mS@HpiQ;dNp(lqC|Fk3Ij?o}C;+ zh%=_2jdUPoRt0L0DablpD?Dq|8I#H)R+E@1HX!9Cu!h7Xey-Ra6R$jGnHX4$o)iNW z@W-R_oCjrx!>4O9ya z9uafErkbY~BsF!kBZ2Y;mFhJTRup`pM?P0PQZ9vzib&R&kVms`3$WX`tD?wwvJ3{5 zh~S)%mvIQM6DzMm67n1y>m*N7B0vV~?^1Nu$tsoo5EmO%qP$0RPc6X3hD~NjtPSu) zr(uj#dxcLZvLD2@*C~4po#0Vs&5jteQ$p5aNuBa4P*-SfctfTo3{t_wqEd-?U~ENE zgPSGl3)2a?Eh8f42dcYcMiGbVLl22=&k&<^~INQ#)6vx7S|3`F0=y)I4=iZ~g(xf`s(e!I5#NnC zL}|UEKi<|`FAr9G^_|&8b84VSUDz&ZD?r6=9H)_Y)SCuqoBU?jdOPGzR*?dzzoFAv z=dzwdQW3}^l^U#S6i67FkgC3tDeM zB6m91VMJE!F|fI!FpR=hcubI1HbMQ;h#Bgr28I&T`}N&6H(4R^mqI(kdOJ#Kdeko3j=DmE}YBMF&*L|+{00j_O=+0cxg*bwmmzLwIn0?1~-YQ zmF;3i$HqG4+?g7S=M4FTn6k%E-ogkI0;IBIBTi=PQJJxH*~rS}Hfk?}9vOZUNHSC6 z5ROm<`Ht;IF_;<1up~?5(F|EpLz0D#oI9-Y_u^XI6r{ESm2ZZ5q@BeY9n*pdl5DRI zt-{>YrXsdMLSodHv_^%0pycmsv@ybIqe;XpTecV5kRl}R6-dLO6vsu?!ui!SHd~ zHGr5*Zso<=ny=3zC{GA#hayVhP5$nmx@l3V-cDkjMCwMY2&C+sGAX07r!+#iGrbXe ztLz#}lv!uk($)o9N%FSc*fvI(1R@}9$QX56xfiS~bx5kfL3*kRB?D6HeHaqFk)_%t z8Dk@t@K% z#WpX#ZkjjD$TMyG?M&)m}x$hP-5EYE^-o7%o;FcOMaq*J5F5KA< zwLiW#A?&46rIU(l)c4H~`^daX}aLXfi~`lRuE2KJxf-_;K6Tb)<>y9MU#!Q&v-XbnaPPI(>+wGO z;)}m&ywx!i6#V4g?`yBeI^JH6HwujRyOUpySl>e?+xA*x%1VE`#8jg_D{TMw_?f*& zZ(i2-u1WNE5z%ch+a`WWK1){X`2Hu!U-s^dMox74`K8wCk{|r?k)+^ZJgA(&;{Jy`=7hr&Rs!$YcA;yO@G!+!pbq~(nJSv=v>mah;8z4ppO$V z+Eew8#?3_c#*C zxi9a$LVH9Vj~Y6cZ1aUO_Bw0Bfv2H!$#!3eLhHT+XN~*=5iK!;hC}C)R(8-L4`*R? zYFtO5yhB4Rz~yl*ok;fYy|e-GmYnU;&C;-G=2%bNp+cw7f+l6?ToNP?iQCY*q+h~0 z2s3mp*-jqP+o5xb*B7I)W{_v-TrzYn=^EeGDbe~ID_INSnsK{g=v*>%F7b*3D_I&k zmnfOnd1oEV58u!dBsu2Lxg>0txQQ@yF0t;Qs5YdVPt@*=n`F9AK6Eaz?!I$_Z0KA< zEv?Vauljrpol8_n96Fb5?%B4YN=%ZfTe{plbS~+q5_j@cL>fAmboT$-vZMwIs_$## zDHt=Ps+mF0pP)bK!aD zT!QEJHs)H^X6Rg^h8#oZl74HWzMn(q603X1(79yjToSglL+6sAbIH)Tq&bhZ*$JiR z|HpZH$P>^|;ex0Fkw8H%pnR~SyDL~8A%o1~ x90j8xFd70QF$7EuObiVSfyl(d*wpX~my2VFqpblPaB;N literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-link-parser/99-default.link b/test/fuzz/fuzz-link-parser/99-default.link new file mode 100644 index 000000000..92fcbe83e --- /dev/null +++ b/test/fuzz/fuzz-link-parser/99-default.link @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Link] +NamePolicy=keep kernel database onboard slot path +MACAddressPolicy=persistent diff --git a/test/fuzz/fuzz-link-parser/advertise-segv.link b/test/fuzz/fuzz-link-parser/advertise-segv.link new file mode 100644 index 000000000..305e846a4 --- /dev/null +++ b/test/fuzz/fuzz-link-parser/advertise-segv.link @@ -0,0 +1,2 @@ +[Link] +Advertise=hoge foo diff --git a/test/fuzz/fuzz-link-parser/condition-memleak.link b/test/fuzz/fuzz-link-parser/condition-memleak.link new file mode 100644 index 000000000..523e4ba6c --- /dev/null +++ b/test/fuzz/fuzz-link-parser/condition-memleak.link @@ -0,0 +1,7 @@ +[Match] +OriginalName=eth0 +Host=hogehoge +Virtualization=qemu +KernelCommandLine=aaa +KernelVersion=4.20.3 +Architecture=x86-64 diff --git a/test/fuzz/fuzz-link-parser/directives.link b/test/fuzz/fuzz-link-parser/directives.link new file mode 100644 index 000000000..5925e5ad1 --- /dev/null +++ b/test/fuzz/fuzz-link-parser/directives.link @@ -0,0 +1,35 @@ +[Match] +MACAddress= +OriginalName= +Path= +Driver= +Type= +Host= +Virtualization= +KernelCommandLine= +KernelVersion= +Architecture= +[Link] +Description= +MACAddressPolicy= +MACAddress= +NamePolicy= +Name= +Alias= +MTUBytes= +BitsPerSecond= +Duplex= +AutoNegotiation= +WakeOnLan= +Port= +GenericSegmentationOffload= +TCPSegmentationOffload= +TCP6SegmentationOffload= +UDPSegmentationOffload= +GenericReceiveOffload= +LargeReceiveOffload= +RxChannels= +TxChannels= +OtherChannels= +CombinedChannels= +Advertise= diff --git a/test/fuzz/fuzz-link-parser/oss-fuzz-13878 b/test/fuzz/fuzz-link-parser/oss-fuzz-13878 new file mode 100644 index 000000000..dbb2abecb --- /dev/null +++ b/test/fuzz/fuzz-link-parser/oss-fuzz-13878 @@ -0,0 +1,4 @@ +[Match] +KernelVersion=t +Virtualization=q +KernelVersion= \ No newline at end of file diff --git a/test/fuzz/fuzz-link-parser/oss-fuzz-13882 b/test/fuzz/fuzz-link-parser/oss-fuzz-13882 new file mode 100644 index 0000000000000000000000000000000000000000..7c56ec222d8ad80b37986f731e523f530e62f062 GIT binary patch literal 2015 zcmeHI&2Q5%6wkONyC6=8lMB)iXnSh2B%OtVl$lSBjvWddS|r@oU85AIvLmF4f1dZ8 zrle3nLpyNbskNQ>z2~2Q?;~EW3pmXydl7q1_CAcAc;4-?=h1IGroR(5T;%rA{FdiE z@J0h!O&1@7K_)W!SyhWPcxLi1%9u15UaeFbyee&#TAK!MO@5hcxjaX`G`Ntcd*{&P zTG>@q<)xlaN@-@yF3XCo%!o9Q{5{&&M@Y$IA@d_L zBBGK6pUBqH)^9>|ri@n0_sUo#J5BH2^G`@4hPYE}h9GX@JP@%@{|qv^3*v#qj|3pD z1g?1|<>gDIl{%M)g@TN(+{z-#q)Tm=im|N8E_dw^`s=$rHOde@0#7C=Vk8nDK>>G) zHBBKjR5-w>y|HkNj!;=-zDTlT0H2(XIC{i|d#G6ig8*}({m7=`b^o9SQca0ravMU1 z(+L`{ePWn#jbnuIqL82BMm_rnAwHwu9!|abC=l1EsU@3Xt`sihY^$jBS7s*CDppd# zJ#iIBiQx>sZ|tl@vekQy-2ZziohLlBL5N_ti(WU~fRL`i{u)!ulWQPb{i8XH7@3q8 z!I5p43CIHm(klJ<>nqIiZ`7>OnUSNOw n-^iNk{g)dVHg1wk;-}>XWUCh1NkvuN?|Vrg8GC`BP!8}7-`O(3 literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-netdev-parser/directives.netdev b/test/fuzz/fuzz-netdev-parser/directives.netdev index cd7c3aade..7da3955af 100644 --- a/test/fuzz/fuzz-netdev-parser/directives.netdev +++ b/test/fuzz/fuzz-netdev-parser/directives.netdev @@ -9,6 +9,7 @@ Mode= [WireGuard] ListenPort= PrivateKey= +PrivateKeyFile= FwMark= [MACVTAP] Mode= @@ -51,6 +52,7 @@ Name= [WireGuardPeer] Endpoint= PresharedKey= +PresharedKeyFile= PersistentKeepalive= PublicKey= AllowedIPs= @@ -156,3 +158,19 @@ MACAddress= MTUBytes= Description= Name= +[L2TP] +TunnelId= +PeerTunnelId= +UDPSourcePort= +UDPDestinationPort= +Local= +Remote= +EncapsulationType= +UDPCheckSum= +UDP6CheckSumRx= +UDP6CheckSumTx= +[L2TPSession] +SessionId= +PeerSessionId= +Layer2SpecificHeader= +Name= diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-13719 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-13719 new file mode 100644 index 000000000..adff4c186 --- /dev/null +++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-13719 @@ -0,0 +1,6 @@ +[NetDev] +Name=w +Kind=wireguard +[WireGuardPeer] +Endpoint=:0 +Endpoint=:8 \ No newline at end of file diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-13884 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-13884 new file mode 100644 index 0000000000000000000000000000000000000000..ce8d713a03589ab55b179ec7fbbab95521b69070 GIT binary patch literal 449 zcmb`Dy$ZrW41hc5EgakxrAQ$|l_FAGoNg%{&R(Dg?HydN3ci;w;FFmSmf|9cS@Hw< z_!313MJ9b!mq6r@5=!zteA~2W3j0Ypo>g>1Yoc+$()zOO=@B#z?0YCr{00aO5KmZT`1ONd* z01yBK0D-+jfQJ_#00;mA+k$|&ErNoafWU4e(Ae$nLGShf0fD?kol9Qpec=#MBw51f zg1(dCiF|j*OF~nW2=2*}Dh~-lo$@J}a2#SVuV(D?1nZnOQB;vO@$xNgt)}$8h#Ki> zV})QY<9~g>59mT40Rcb&5C8-K0YCr{00j0D0f-d$a@e8MfB+x>2mk_r03ZMe00Mvj zAOHwNAOMkK1UT>&AOHve0)PM@00;mAfB+x>2mk_mi2y{3dpYbWb-MGZ^WQ)JUU76w z=O*Gq6pYW-RuT58wnd+iU2BLZHH?hj zsoFcqkf*d*9(UVV5-OFBA0WqFa;T+<6J8^FSL*fip%jYxS7Btz8vkgFf~SomszV*#4 ziD=VIwVErw$saj`gR5C4moGX8D0N|~-9Gbz{w(0D*ZNn!6X@?>u`F8-JcH==+7}9J zwS?VQw~x(zV^~8>xk`q}IgFK4V>J4a?~)2=gHXSgiv~ z7LOrvIFJzCaEqh4 zQm{C0BHj)aeYx{pN_{4a=|NNWPgCd`75L^|@y*Z7jMp>6l|hm`tyR_3*-+$s9VhaG z?2Dc_bH@@kl}ai5(rG`|K3Q8`IQJeV%~RU4Kfvf*k;fy9Zd9URWUpBmdl%7V?W86M z3Q<*zEs|eE*qsAX-5GyGjGAkBT$=9$-K?IB>NB&1Z+m~ z0BVi8+nGj=%F@ZexfBkJUHrWkK+<=Gq{B`Y%hlKujK>kh;lEF&6LEwDlD=w^zOl3T z(i12wk-`$GxiGe*XPNV%Yar<>4YW7bhApJs#MsU-FcK|B4TwrU2NVA8PUK*F@zKG z!-=SjsC92QyaNJ&03ZMe00JvPK*|xVms1~T(c3#Ar`LOv6<6^$`a#Gtr=$Dpa#mP@ z08TAW4KG)vQq*MS1L6q&0!NvP8=Pciz)C4qeIu!DWWc7I*D05O;Y`*bZ%=>&y*~{EYm(KGfq(Fyna=0 z*E!S>aIu1}Z|`J{I^RMe``uBQMIS#lG3(p|#b4pc5A+r&p{59Ag0m{4y6;+I^I0Za zF-pOQIH|OBHSXjTUsqRXXsZ|AQ=~hEIVLw0y9>C}Z})@2v78m0Lgch0cDgKLk)eg? zoKZnNbZ+kC#;TGVnpDc@rCxT>_41M|aWomy!iBR(8JM5wgx_ejn>G5Z1%tXYiw4gz z<}zBqJ%|u3>Ix7d>t0Rskrh6k;~|)9<0meq*qDE3)!nx;SXdL!^F1FSkz3lR`2&br z_+uV6!0Dz$)Iw&ntz=M?KbYBp?U6oh3S}X1+TY|@B}D0F6zZtJ6p!cA8=Fg^%`n)sDV63;#ibHXShDWK-o}tu z5Cjka1OS0Z1jw3lB|zKt#|^G@g<<;=pHITtXAgk2Rzg(`-HcAjkbh{?<>0&;X)Wve PvMEXgEAqRn_k#Zejr1uw literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-14157 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-14157 new file mode 100644 index 000000000..d3a65e1fe --- /dev/null +++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-14157 @@ -0,0 +1,5 @@ +[NetDev] +Name=w +Kind=wireguard +[WireGuardPeer] +PublicKey=e \ No newline at end of file diff --git a/test/fuzz/fuzz-netdev-parser/oss-fuzz-14158 b/test/fuzz/fuzz-netdev-parser/oss-fuzz-14158 new file mode 100644 index 000000000..6f6bf723b --- /dev/null +++ b/test/fuzz/fuzz-netdev-parser/oss-fuzz-14158 @@ -0,0 +1,5 @@ +[NetDev] +Name=w +Kind=wireguard +[WireGuard] +PrivateKey=E \ No newline at end of file diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 209132f23..ddc60a9cb 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -7,6 +7,9 @@ FastLeave= Priority= AllowPortToBeRoot= MulticastToUnicast= +MulticastFlood= +NeighborSuppression= +Learning= [Match] KernelVersion= Type= @@ -60,6 +63,7 @@ Protocol= Table= Gateway= InitialAdvertisedReceiveWindow= +GatewayOnLink= GatewayOnlink= Type= InitialCongestionWindow= @@ -106,6 +110,7 @@ IPForward= IPv6Token= Description= VXLAN= +L2TP= LinkLocalAddressing= ConfigureWithoutCarrier= NTP= @@ -116,6 +121,7 @@ VLAN= DHCPServer= BindCarrier= VRF= +IgnoreCarrierLoss= [IPv6Prefix] Prefix= OnLink= @@ -130,6 +136,7 @@ PVID= SamplePoint= BitRate= RestartSec= +TripleSampling= [Address] DuplicateAddressDetection= AutoJoin= @@ -176,6 +183,8 @@ Prefix= UseDomains= RouteTable= UseDNS= +UseAutonomousPrefix= +UseOnLinkPrefix= [DHCPServer] EmitNTP= PoolSize= diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-13059 b/test/fuzz/fuzz-network-parser/oss-fuzz-13059 new file mode 100644 index 0000000000000000000000000000000000000000..d4267ffc0e39e4f3e76980d3888b89cfb8fc5b30 GIT binary patch literal 6558 zcmeI0-*3|}5XbWXFa0b0!UL63eD*oEAEHt5<89NVfhx32qHGZjXe~6^`1iRiIPIE5 zZAptX zMs4+(&?pxd@7|oHU#CTOKTJOr888^#z7N@n>qK3*_nTzw!H7Cj_l$*;tQ;(jf28qU zUX170r62K7(O-}Kcv%`aKc=mI)YhV5T?SHHQxx>GB?{+VTl97K%li-QIdInW?8C)o zI2t7lDkYUdZ3PFxISSBR8xX>x<hkzQ5jL__nOr-?^SfKzA$!%>S1KPNU zS9$qEO%t#x#b1i7cZ$gY=k;qhP6t5Fw^iWIa&rlkOOUP}T!tVyO&p@^8lzrjl&U8H zC}#&?uhx<6Q_zy0Lm)?@wuW2?$O(MOz#v8%k!z{BRSF`A94>1p)9r5SeJ2$2lk3?y zWXpKI7sP(e|gcIp0sV zp!cS+7iZu(Yk7Iwj&Pgq{d3jZ>!sLpwy~4&dbW27sNaB(SzW!Jb!`TG-CWaZuV-G* iHV&P!#<-bLRJ(yX^E^wn_i#xyEmPp>Nm# literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-13354 b/test/fuzz/fuzz-network-parser/oss-fuzz-13354 new file mode 100644 index 0000000000000000000000000000000000000000..2274fa5bd978b27ae12c2be04794f0d52155aa1b GIT binary patch literal 918848 zcmeI5UvJyUmhFXun}^N^=-1&x-wR9t^^g+v??Fg1nR|c%I!4m*^g&=Xa5FxhwBe3@ zuIwatzWptds$z*Mip4)gk>alrc$_n_EmN!ZUVH7ecX?j)-}dIeZvS5x+>FG3-ygPj zzdwKd_rL$g&Ew;Gx*2Uo|1GA)5z43k?d1LSD>5t!^Kfk)Tc}l)$ z`G3~Y*NXr1{NEnFe7e1VT>tF-tbhK;!}jsk3)e5i7e9->!&^^$O#b{${FDzL{`Z@U zo6nz~Zoj>_`S^4jc)`zK{yO$v%6}yOY4N}N+pQOWk@&~rpWk}pzuiVZ>}LIsoAuYn zr`Pw_(d$^hn2G<_{_FVX=nLfQ_+)$?>Z>OQUmg7->Z`?XA^vLkE##{+{r2kD-+sF^ zKLh#I;(IsuyDj~2Ih!uS*=#zSg^Sh14<^1JPJ)HMSO$~HDqQ(N{bx9l?`8T7gK!Zp zr}J=G;$hAflQ0NYlUWdip?KMg<$M_|mhzh?&t-?2Lx3fZ_&=X+Yd>P~0_3ZHC4Wv1 zfaGgSe{uPh*&`4gLGfP=T#kdOFW$H?oaX%ubH$D~uL10-I8yu;7#>d(kP|^o zgWNa>eF6Nb7c9eYz6e8qF%HmVI^KJ#j$CuZCydHbo_5H((+&GpX zaa03}v&DS2T1~^*Y7vS@D1J?UG6|+Df9{J9xj)$O1_YCZ;i0$k zumVxtXAS#E183kYs*znsG;jvaz}ax*pswZ3DgqCAGb5hLAnvwiE+wggvy2W+#VJuL z60V)xrSwy?HWRlHz!^BpiH6`TI%jdm%R!M&JnK_D0|32fhY1acghoOuN@%yK-rnEEZY=?- z9@t38n<{iDm8R=BleJ0W3Iu0IO_S;4A#aj5b$jg*ZA`kVRBR~YGy!Me44f&;ciJJe zKUG-6G@tuky{RISVk-CH%no~UfcBW%k$l|v5dbN% z7f}T1o*?C5?5R&Zgv_u&zDXJC2z7)yLLEhLYh*l=yh%%}s3q3K7%(_h_7L$$rKi39R6uY55X1m?*mf59Wqqq{NW=+>hh$4!i=W4V=S{#r3cAOF_LN<6(=|=g)^4SR@@5>&Pw5orHl>W zOwes_>?%Pc2@gOLZmF*v1C{1+5+Pk3STYb92u59cyIs4T1v&{VdlK1Dsw+{%>b0k; zc>K;v>g66J5r*uW4S2DX=BOSTIBUMC)jgbvVClkW(WBc|86kB(Lb~58qh2#j$0-Wq z>hYjI4M2aA31)l3+qD7OfPedV%^t;l#wqf6VHfe#53aAH3FKRl%f{gZX#HGdUTjZH5+WyFN9if2F_wS((b^0 z^STB*B@Y&%W!DfvE+LmXxn!rLscY7JXrfmsdq_z8W@(?Dk`cepn~#Um%Q4O>brA84 z((!;A(n9J*3#p~qA9hMAv1ljFqxx$rZBWTj$$G1ju~SlRzY{CVvx*SDI(&7yB7{+R zm-b}ARqTds3WZdS_f?;3UCI_YT)c@ztyrU<_*MmfH-?51oo>*-Yy~t z&wbW&r8J=_uA>JCM>9mA3_( z#(_JkkZ9kdNaR9r z5H|Lr0*X*as3X+j7T4Y4ECw0p1A=ZF-8Q;yblYsml@e2qSUw8WX>qsop(0}{MaDts zFQ@aV7c9eYz6e8qFKH%rcXO;i1?*Y+=tce09+yJ$!v;T7xN zHMso^N|{;;kgafSehBdjZuyXl1wF%COr|DNpF^e|{iSq?N>yL_k=cZ!u0^zPIbQ~g zrHqnVc5f*SfU`m1Yyr-^N)!Z!7O~eTE^S6IG%s((ls@JG&UQ%IpN5h-uF~V75}*<| zhf1I{*?^yweq?>D1K?+R`$}}ZQP5u8d_+N8yVRl&)!cldJIp%3)TXGWoGaa#ghoQc zu5yw<)A*<~JIA~+@r-yzJoCIS>>QID7|ZZVge`xi z)waMu9ifhvA(6~yF32c_S&G6^l-upMOAQAySZL;Y@u}T%1sfU;KXbn3Jz9vtK*Uie zHoD}|$1^x`8GFYFP0mBpzERQDBrK=9zo+{rL$NG5i4y*DTToDXjmiE0M0}i=x$btcWeM77A^IaW1yxo{>|r4;(ysKtpjHRz!{7hMxEJFSwh29 zMTiT9peA94$TUD?nhl4{6&W3<90UsAr z4tha?l7o`d#%=*{2F`|svwul+^VG$uI=b6>w#HhWNCYSZs5Z_aIO{&n*w#m7raBW4 zGzeN7i2Z{9)dBPN63VONf>KN9<8Hna%PpcyLrO(Idd5#RXL{v8hK5hI?xcqVXm zH5uomUKy&2>0KjllwjmKAK;CuIHp&V@Fd6LsljWuP=RHpw-(NAsxKC6;l9wTaE(!d zv*=;a6~EYP`wp{UqmrVMvg4Hyr|v6~jgNc0zt5UDZMc_xX=GSD2hO^WGkW9$%$r{# z&iLo;{oNag6AENw3M9hTx83GIRe=;siWYe@(=|ba)b5=?96}r_5{KZ@1LQbT7o+?2 z0=H5O1~+*VF5L*n>F5&4bRm$v659fHZ zy?6NNVEHJQ8;yx)(fQve@$_@)=jz2AX8&}7Lpmu&-Xw40^-cCz%nOGi@`Ku3Nz@}~2=$*RSDy~p5Oa3oFG zyI45g+jEjAeoCf<_v&qXFd(^NHNk4q;HnTPz33nB)`AJT6#r#h)DOYZRQiz>;*2fZ zQCSBB-E9#mgdin?5~P$3icr^lsB?ugwrnGLq)ITwH(R!&{fTh9&QZ~Vyd(cB;C6T3JaZ78QHoHC+KA5J>;PwtPx`WD8|AwK*F0fDYfJea z9&!4@mTh_G07|zA3Ju-!XMJFYNzkny4}~DNQ|L34u8bSt44i>8#!p1?S?F*-NgdFH z_Rx4CtxOWi5^M8pq$H`n8sZ7@WW`Lu1TMnhfiPG!iewXv3ZA;nw;-OG%Q-A#SjKu| z88bte`TDb(uiwqjF<&2*y7@=bCh3tk*%zX(>(cxsI4h%vu^G9(%3){bDK4WSVi2+X zXis5DVcCSbSX7OJv*vNeVh|RCjMG(y4t%fF0)or211ScXOImv}oUs^03m}RAPrT1B zn8V0+q-x3h*u2=e+lIfS-#8QZXkA&ABI6+Rm(%&w3zlIxUxcB*m@fR)Y!=Lexjd** zx^j}aA75Vo@p|lS*Y^+k*S6OeU1p2<|#7qbmQlnXO}sAeP-{T?M|0|BU|Cx{1D<5-103d7W52lDGZDl7~4oNu5mW{ zi<_8jbwy+Sn0*-a(&{`*u8C-+IAOS)FN4KWMoBHZyG>-u+xSXylbjeXUs-Yd24^&6 zXvnasop@Gd_K>_;DsL{xn_h8BHk4H044k!5Qh_sY2F|#F?ixpFj~%5BycY-#rL{mC zogM2sfu<_(&ni5$uIJ4!>>R72`?+pCmL?lr$C)@f@e~q`$9$WP zLH7e^SSi}jW55|W17|8lefs=q=L*0`A?cCwo}8o(u(IM7xI-H+q*W`g)U&aN6okMV z=NO!H(L}ustvKHH?tMJ+>X7!3_H>kLCcz@VdZ};%&cGQsi`w>5G{J*=)Wm?twsZL&q6=4%u_4ttoThwLeLN^>K|cDI%O)x~aSmQ3>Lt6lKq$7C^F&$iYb- z6h)6u%EC~p^;@fE1ZVU)+Tijvakkm`ql?RT>t8pUAAa;Un)J?s45YCTQe z5}(FWSKj`@QbEB<#=aFVaL@P@i*rUtfA;;Bo2qtS!M-W>O|fsPMEGjlwaWM>Vd*2deobgXC!Rp2(SbHkyk=9k$WLQEiq1GzYVg!-Rd0G{;Dri-dXjK$lyN$7} zg6`g4ds?*C)5SN39K2*=Kyn$ns|a0bqLfU|QN|D?b6qSPd-eR*n_EN~IuWDQtw&qPied@>89 zRaO@o(~Ggv=Bxd0UcWFO2U9;-_+dDeuTB*pQEx^GB{!cxiNC%`e287{TltQ*mXvrr z&4X_og#L0mpL)SE4Cjk5^cT~Gznaa0c`%o7iQcYTFQ(Vx)A%oi0_pjP8+?^Q&y*W|S}iTY~dS?pQo!|Cqt>1L-SJ0<%=wf5(fk4(iH7S`Ww%h zq4eOarP-g+)y2)z_lND>@6RtT->rY$Y`h=6&E}){2w@ga;A&S|5$a4!*quL~!4+oU ztT~*qQxeI82p*XL=yTBLIE;n@7-u_)Qnqi)PD$DF$!g;i<5WUz7|FmHIIDrPt9{8R zdX~!kx3|$`fH-@0>y57_BS;`IZ}=y32{22NH#v{`~LnqLs#itCPN!_oY(s`cTRnITX$r7fE7Cv z<*|I@MsWBNDMNqW-rv2s7`ie9&K%+l1(FRJ`-Q5Tcm~eE8938UK}u;O?uk&JseBMn z`yrt2pXq8_EE+GQRj5^c9|~y->B1`|Z9qbxCQ$2QQQ}T4cXcT^6FkVteXmw~U?^~K zh~&3+AfBahzXZ^w1 zq4Bh(e2AKZn$t!w0%zc?CpZ&rCAnkmxP~$gLVr1(PrYCnhVw-j`itqpU(II0JebQu zqDp0FmAM~ZUjOlW>}}Wg4=)^wF0;jawpvZY*=iAr-Dl$0^e2;Gy7K3~_>lX9P1i3> z7KVpn`>-|3eu+nV`1;Hv0sCVE>D$igk$h|!-eQ*Ham81SjTs^q=wF?G3|-@F^q115 zgH_DNv7oPKP-)g2lRJ4MK)9SQgT=D-M5fd@a8~+g_7P{?Jh4AaZ=D(#&5Y7!M3Lhi z{#gR+I&+pjoW!%zkBoSRmeLa~#RjU9OBJ_VsW`p7m3g`gs?vibZ<06l;`>NVR_nlm zGjImZT#W!bwR-AB1&*njR~TSj$GWbQHd9lp@lk1Zj(KAQ2Ly*Us^_lb>%_a$#PYrI+r**(6kfps))3oH7D4}Ic zX2Gra=aGS|OPCu6Q$JYvVK|kqPQ`?LH%6C}tACDnE3;;nHM8xt(^UjGLYj~ukRbFT ztwbg)#Xi9qI2$6)STc+2!r(#BkAf=a&&G9;E-X!bCbHBj{%ud^x)8|^tLW!Xy8^in z@0b)lS~92>GU}H4$}v#WQ+D(DllWg=+p8*- zD!M`AjBR}mMvI7NvaS8Uo=_~2~Up>>b~h-bvJ zHjXAZ183k2W6hh3w2ak7EY&tD)ABcwg{~O5=me^_)S<4yPRWCyn%y8!`jB{~ixuj- z(vPf=54B_f9bH}AJbi!I-u?c3H&^XNbJZWkM^Sb~qR3`sur%TgJ@v&Cu|?N(wymq> zLI{$=+DKt|kX_-7#X>~zv>d?QPZPgu%Qh%><&0QuEubbNWwB5m)i-g>G^(a00?mmq zoK2T($7fDk)yxfQoi*dz*_+|6_UouLF4fk$&elrpjkT)pu^`p^<RD3d_XYo z!@Q5)smvLE&;|73aujlT5El^_dm}Eg806cF zWbe50gDbwqVi4>_A{iuqk`ea`9#h)!@gP}~H^6xoGf3Fju(9b?c*N?kOYVZ1qPWDf zHsir&SYN zz41a?QAc&%tMmB60pb8vIzW*L_*Re~(@GXY9s_6K44kn7U#Ns*3TYZ@G}QEHNFnW< z=u${iNb5%qVUW9wxyv|CJUHHVp|O3(TQM1aRbXSaSC<(%VdSJYBPZ6v61=VquM+}9 z2gATGA3iv!h;2#=1^}uI0F3Wnzw~x1@pDDs4&qGc(rx1GTPpd2vnn`yX>+eLID^(e zYi_sSE;Ss;sz7rmiBIF{CggV)Nq{nK(Ejh2Z@nuq1T0Qi^5=wuz`u#reh%~I*Q&B1 z6bK5$tZft@WTX@tM-w)*qINo|Nj`A#?4dv>Oij_&S%;~~=35)AX zl2wZ4Ju$RnFle*mQzOg`6rbsdv_Flc)9&KTQJVVvNs#jF4$h>Q-`zyeij<-RnB+jD5$;t&1 zCxw7M#}+q~Ww+u+uaipV&4`=E4i*Snyo+8u4hl|_MS%ukRK>|7m(o5xcU6*?TrSo9Z_4jCj^hLuz6yY^4Rn zA>okp^N{SXYJ7^y6(mt^Hq=xsJ1#Ye#aA`&2_?mFG2 zls;f6~WsZAl_{J(T)KB5E0&!0GR{tD3DB~dXm(%&w3zlIxUxcB*m@fR)Y!=Lec?B?* zvr9d`y#C|$*xRn}AM)=PtFL*_7W3I^H4SI0MJRl-;@9*ilVG~?=f3!mE4G`G@?>Fn zC~nJ)!`EjnGxj@v{XAp+Cl(M1jMO4yOGP067?w2bi|?8IIYuN*>9dGFl#AGG^b#9t z)FV@GvpFJn(sdDHJoJhi?WWw{dgkvn&M>$(u%R#x`A33_gk&W^lY!DJ8)`jBQ`$9A zfK#*{TIwt7+=P)a@ibH)oslu|H1_TdC!XyDKl`9lUjsQ*5%NouF2}P93IRJMrLzYZ zX;M2Kd8>{*J0(v++bV=Pc1rFHK*hxnaj?5_P!MMqZ;!q!<;?gE2cLt#yPp%qP}WpY z310T9^DfyZ&aGZ!r=-?50%)brW*=K^v_KkYfk>n@B&f4f(m^>(Jd-FDv;(gz78Q0% zmf^27q6g53?v^D*{r8H&k@xaG>Bk1rNQYrss&DH167&ai`(c1 zlRTvyrJTO%=SWBdXBl7Y&XDQC{V*7>Cte7=5O^W-Hrs`O4I?= z0X6CXBvdxW>hQ_0UkEha4>XN?uHN5Q;rIv+2o6yo@y16~sjdn25cC3q{w3|t1JQUPtvnv328ZoKmm!ZK*qmChv0cVRN++drWlbzSw|k!yz4wF6hc}Xz z7%eecVo5EW>0=y`^J`I6B$uRJk}k6y+NH36ZEr4)q)*asLL(?*O0IF%$xAQjq84;f zp<6`?_CFW-$#D0sDL4!$#~Y%l`x~Li<_tK58J!n zpYLv3_M)4XKZ=jB32(SnWjoiQK;6KZE*PScJw7$n@7@jBMwzAE*3e0;3y=s%gki8A ze*pdf`~h)!aV!aJHvVWwfPaVxFgJvAL%1LghszeT+N%qvoU8$>Il?Ei;5K^cf>ye2 zBa~sQ<6!Cs3qK5}^3_>9-7c3$z89Qda%IgdYi8SPr>h8XB#@ln8LJl7mHE3%LA}f{ zW0zyOcuT+;h0+iUC6>(Mx-fVU^rN5z*)rkYMPX!0A)?fY1>W(gJyl0IjJ=`5FpH)C z(&AS=x)`I@58@p=_0U)Dg(lV@FJ#m$^_63w(mYO@n{7k}T3ox?8-ctR$!b;N*#P2M zh02XiDV|u)NozTFObB)43jX{l zC5n254UdLLQ?E5=!n_s7Y;Tv%F60`7F6y-b)oa^&WC$i>K2{9SbeGAnR<(m`RoT|Z z&LX84QwSst3mP{xZs2Dxu-ql9l|%_AWfAeIo!bIMv%*8e+6m6y?ox1c&Nd?kc zsB1pdxiU?$SV$U3>EgQ^$0^OPw%;zb2rCO9#5>U`KOnP+Du^nrh$<`=k~(JX`jPk+ zES83_PJBD6R*^@KJ|0|`$6c3Kw)WKl;_R7l_g+oLIeuvws*2YKAdP6my<_tixE4F; zS}fXip(>u7qZw`Y0B0;b(zm=I6hNepGQ|NB9I@~Sr3j^{6{U!k({gc(!$8ycxX1hZ z>kN&s;a>Ko5gisfEbFh#<92PGxoH*Rm&mdE=k5L7n~S|Wm>tG53PJc8{0x4UD5#m4 z@FU_+EdoM3J6b2xQ90gG$)GfPmTCbpc~d+M_NCAea@i1S!VT4UA+5TEYU-9kIw}6y zoXFOP zcC9#cru4)X5@vd#WLeLA0QY?F+RUwK}C9%x6C-DO&l#I#zMB806dTqIIQesrQVswlLpXq$Yv0SlummI6IbjtOo>CTuvnIojN7GpJ^N4!3Z5V17~fh<0r(K zgHuDGp{LZwsR3sPIBR^KiNzq^7@Qs841zwuy!nO2AaYGo8MhQ2V+QH<)gC$1ih8S3 zDUjeSZm(h2#;%QB8@o1k?J5LhXaZ^z+C$@ov`Xo!5o{y+*{?9bs>p^-Hf$zMB?KD; zn!+ufS zyrYe&FgO=IjCd!BgCt<4-RbzTJ&7Zpb(hxkByU<>uMW<@SsfqSZ(_Bd&DP$EIIDa? zClm+@1OT(xzfjfvF-aYtUdcDV1^Tt3|_s9h$|O zE4}2Ysz|jfCWbS^888AyaeF8g?J!JP8>TQAI1rUp-F9S57r6tuV}NppBZI-`Poh4_ z?%<3{mUg8S4u1N`#nX@f!u2uDulodN;H(X4*cxZs{En2(_o$_tAV48Nu^T6G$cR*v zmcjO?>TCokRxUUwXJ5%SWKKCdK%5m#b=xGpo+$tkofrQJ;)NIQf?Ik75U%vBn<9e- zp*kdc=~!2L)Fz)Yvyo@kxG{+K_L;=cKcxF)38{t2oD z)f)AMY7x%@FCcGX?$dRqND$sR(Zr{Awy46=v8CCc(bdJx)Axt%-S5vYF5j(x-E6!c zz0KyM_?Yl5m7eyRB7-B$P`5{e>%0)fB3h#Qn|oF1xL5;P7%e>t5`y zrlm6_5~lPiGU-wJP%dJ-TsFxD97T%Ku`gXSP{r+GO2`lQS?NAp&bc(1OOun#=MZNL zaOPDyuzC;|q1#=8&PE2_u`|p-E%lXSpym|y;EWLqMl1~6PD%;IX`;RcQid|>8Ayt_ zZZ!^7gcPc96)HD&O3DcjV5CXybmXl%^2K9OUFtdvILtADy=xbdPMxMx$2b!_$wiuC zr)1J1Rs_plF+>+QLtUT(k9K_Uxz^39xngKp_Mu*5r{wyDO3N^2DJxiaCsp1un;#f( zHqC2*vjd#5Q__KV4xAm}tg}-xCES=aZfM-lxS??)f*ZTB`mq~p-voSwE8F)Tds=XE zhXM8G;Y2X?rg#ELMxzekk|~|t4Z0z~PRX~?`=9(#jCXGbOdnKcl74$hAITNT_1lqK z8lhTkQ7v{#Mhn@HD3B<`i8~UJdhB5yuJL~QscqN6;Z0FeE6`X-HaK=3@yJX22O499*2vm<&W8>FB+}ERcegQzm-u;Tb z@j0aTa;KpR?_F0U6CIA7`(hMZR;XG^GIhNiqyQU`uqnrCQ%aM!9hQA;LKzdwMo79nZz)rMIF%nI)FKG2c-6u zG>qj09{W>8L&)XE%!8Se3Rq~okXEFfLOOT%JO`+f+Cm}SvqBn+g^Gq{HtRw>A)am2 zf8Y$9fivX-?2Z6U-Xw4C7k3oWE(>X{IEi;xh)4*|z*+tl5VJ5?JUVXvI`XZT|EO)c z8|WeEA$qe}v%iQ>V|(T~ZguEpn)>B~i~SLVff&Z|CPHgWTcw-v{p*+B&eEML&OwKu zL-{${3TIZgl7KUC2F`A`-!3&A$S&zXXbl%QS%pf2Q47E1x%nl38+%q@sF*juR`nB7 zaZ_;*u;L~)Sw$mK)0R#Oos{gd&56hE0Coq&`_3upk6lRaq(#zV zHZ5|6xH5UEQ4w>v!eCHgu!qSw2M^7RPmC<*Wn^(=F!=mQa1icSFfrfkPH83+m`q?Y zfysnM?ytdZ+o{3qup z!5KJng0l!_QqdM{51gGH&KAr0GFU7n>5oFnZrQm>zW@Md2LP2sxTeXi8PnzlC__b! z>31&$p`bcCutUSy+a0Xbt8n;??-R5`6xaLei>2B z&B)LT!d;2GQule}+*_s7NHnzlPz-f*P2F}ZTC0;=a*|MGr{*2;^zB+lkX6X)euRXq z_JyqWf3kkF@kcuX{6j>56L-&3g9|c=Z^Sp^TYgD7G}{*&YNWo~*8zpKm-l&gz%h`&bJQ7^))y}mwCK3-)uO5+d!X!9LVB+fW z&{CkKP~#Zl|7RdjhfjV1K%nVHU3nA+$}C#=bOyphM8tm2Wu#XJ%Evjo|Pl zGPV49dw=)l;)V{3`NS;m~l_M9gWtkXF4a zb%g|lbV8QX)dBY9oSOnC&$U0a1BAt*QHzCjr3lU-1P}u5V9ZMH6w(yZO^{j^x5dhJ zlkRjI63=1~7K7+6C~dILdddr&F&fuK3IJz~;f%{b9eB&YXJhz8cjGcpd&SjYY_O86 zesOZ?2w}ig7#QEbe(CK52=ByraB}Z4Rf}`xsT!a^DuT=DcVP_Y82) zfa!GWAy=6zav6((zV>jQrr%jTorJEo?Awacp z4#8RXamLC82jwgcLPkg53b9z|j42xiXYBmdvu=uvq|+kMvgA75NPOAG zjbo(=&nKQwJfBevwy&;TNVEBg0RasGO{Wk?Oab-~##pn(5s`I7B%MR)b4?=>MT}zU z1Y?4+9*pBl@}TsYjyqBqQrO#Fpor{~@xZ9#Eb3;<`FjX&BE z;2$Ca%s{a;_`pzGXQ-OiASD`y56$t)0pTD(Z4OF4BMO#NWthv8JdIu%y(>&z~L z&o4jC-hETlI0*gabUyWhWf;yEVdyWW3x7461@mBD0gQ^mTS4*h<@F!0$KG~*|L`Jr ziA#Mga<-VyR;y_^TP;H2UlhNlKbZv6l|T2zhuj}*G9OG9hKJ&I-@@VRGZ&4vtAx6I z#u%kOEQYO4l9fBK{z*E-Vn1U!I0I+-`*VsImr8Pq(U>E0CtVlea=r`}%huDFdT7Km zgr7E4jB~)5h(zA_NN|y-SVi+P37S!!0gUpr)K`vyntD#yDQU2I)!y<%<<`11XL{t; z^za-qkmM?EH4a4%T`+1UD5SFo0YH=5vBO*0;fuYxggF@`tuup*FxUNNgHYXM!6Kei z>4yf+NC@lRhpri=&p^7fS1fqi-9f4X zwkoloiSqwylD8=V4s?H=8L6;gE9E(m$gHV(q4u~tH61k_H61k_+U`hr`p@B4;~}0k^dQNbbcowX3`4`&!OTeojjQoOT7_CQ4sIj*QAnF< zHm)))o6}Lvw4o6O4}?JmMk^$NqK5{~z*!Sy1DwT*Vw1ZmY{(UPlf0>`ZdH1a;4JRd zeu*|6(74@o031*aa0brWpc>$;F`N~Tj=#bvb?cQ&9pJMue5RgydzBI0t-HFLa36H= z?fJ!t$CxnSDh!P8U%&Krb3M6T;SS==jh7vqxx$&%5`S>k-E_k}6jl&67@UXJkLRY; z@y;DeJR_dvuYoI;1AY@K>)2uwV?~^OeSE5BQ=yYWCxuQ*9OQR`R!f7U#UUeEL9LE8 zB^@OlC0!>vDHAUsZ<063o8(Qi$@%=H1C#-jfp`T?+KY_BA!*SnEn+ahXcdODDN4MR zD0cqo)DTMTtsREPlbQ5&WH9*rNf<}7lgXMFwxF66S@EdNU4=-Ru-Mp-K5nv zzawQ66PwI1Fd3LkxgnfxEHLrrP-g+)y2)z_lND>@6RtT->rY$Y`h=6&E}){ z7{pEB%^@3#>&KF_mFQyBBHJb(V zU|s=?<>qr9Uta(5dhBi2_YW?wdYvuiv(;)E&Q^<1|*+6}g0|+t^aIZIi#4);cn+#ZJj+Se~AE+|Lr$d)8P8@mzAi_SHk;nf>yLVks zs+g+;%T1KqlM0xijLAH-S2DF{-haVijpB`_%-`qbH{li=FBlL8# z_m_hzVVAP>+_Kp`Y=fU~|0;f|&yRoqtk$?QnpJL0{-mg3GpO<+L9X#!E#-t|Rt7J&0S8tGf(M7$uh8UZpF~9hcOIc*E zazB4zBaVv>@J%!!I-gJW*-1G?SyApIBNN~LuQ;Z}^y73)qix<~N*5}zlOx|;G~8#j zQB+aS+^A!sfQEt<6e=3|k6LrYdB%-HM@yw^H2dV~=Mr)GBrJ;(~ zP%1u6YP+Hopx)*q5-ahn+wp8){7G2QKx<3OK8)%V@0H#|k7qP9(*n9dtID~;ie~0M zNJx{UNxi8u7&=BXGwQ4qSU=2^!zACBN-xa_L^D%=l!h{hW@c3UMSlt&lqo>P)qd?} zEzL|Zb!O3zvaQlzNiim;O1Ce27+BUKMgxrPZFFExeA=*L$aB<8^x}Co{XA)AYI>X& zfMnc0%*2CiX13it?ds3_&-m(aQ*96@n`nsi>>`~!3YE&JbCuWxf$s-lOV&hLq0_J| zP&2*e_+6TTJ7o!FNxTk31{*E!7N{;g>$g&t7~W#5@KhX&%2K&AOx7jBeV3mnWl32q zQ25#`m!yj#K28%m=GT`Z-cXrmXF0KxYMuC| zYC^hu2Q$Oq44e&l45Q2{G#J*Y8CG$-hIrPCc*c75ecsA?^_sUtpa0#8tXJ0*?&3AE zJM)Kqq1=!DFQsE6{&T^S<{NGI z3G&;i(2qTy=1JT*2>s=BKJ|iS7|s`A=r5)Ve>Iy0^I$G@oaFe%^jdsscb2W7`1tbr zkJn>wyS{&Tk!z-?-&>h2=Cjpm8qQXWP&`8MYxOn6PLW{KwuOn+&eZ6B*|!{wma{bgLh1d9;VQ2uKrCt z$(kIh1YoWSo^D&}E5|@hy#Q?NHYm-tL#zoYUBMZQTE$@erHMmd!~_`)n~P@^a5mt1 z@JxAt0H8_j2mn@_;B)&9gt=~ox%iG^3Obt|@5I6G#z9Bh6hD6w9E1*tsa!~;{ zR%J`GKkSrL4CW|dKB6Sq3%!Om9icAiJfvY`FB8zkq>HH+wWW?BJ0+u@9w#e_Fr}b# zE1sK4O(BAFU5kK5a;oUto=@MFosxNp0)`4htxUnlqgsfpRVY#k9j}D0;A`1_juPsg ziDP&*$=gL_4@(G-cA7B|VIaal6qiD~JM3F((a>$csCHm@a?lC{XMMsM0ANLiUZ(Ce zI)h+KC3U=#>Vop!_@wXq^0zuN8OMqYWezyfJ)#ioSt0l(YKi`Ndw=%^+gS;Lmb}^h zyjgVnGx4mZT|0GCU~jBrZ^TBc)Ol>Q`5S+TXXqM()is(BIU6se55txXr?BraBG?>V z!&2)(V&%cgqidu`L=AA}?if-?Q%JXQ48a*V180WIyzG-3jyD`{tY*92e!J9gAdgQp z1+%k$iBIFdj1g>{3O1#8W9_@5A^fJbKL{9!=t4(>j>hLtPYp)#?_8-#@%?SftDr z^Vw=O4QH!GDB3sT*YqcoV7l_>zW9*)gAHLkm@Etr#r9!qvkxU6>EY`$*M0kb|(BTV27oPje%l&J*99+kkpT8{%ubKWp&-m_72VTy_`cR|_6 zSItm1=s~Q%V$6#6&7*6SmVy*}JR-9?%A2M(1%;q81-GQpoDcxwH`E2#{QAYUgj)L4 z5NHk_S>vP9>>MMZF(RSDa~d57m^Z($b4+evJah@9$jH{oS1`@nw?`0TlsN zlikvxrf6Beqe756V6)TdN0~?A>cCP@LNF#6>%lmlzNB^Wr5p;pjU=tCB*>?71waHqbKGD8lRhq_6hW2t7fla$Jy zLoI-0+dy>qPu=1FxVHjJTkax0jUyAc2~@G)6s0{P_`&E;(IXoGQ`l_$(Z%Jv^{<=F z4?lXFO%g6y25+9{H2Dl*s9`vpE*A~#?9wzX$tO#shsi9s75}_z^VH*D>IVxy45#wd zsjw2Mw@L=R`TR-z^~FtYdh81L{B!ivvu2hxv*Lbl}weRkb^&jgWrgif&|mC!e_ zir@^KfwQF34nKpR!Ot4uXY7wpA9YOehN?%AR+P2fM5RyX_SxntFyg_1+b53+{?mHA{i zW3dnsJY_nr{Gdc|m_i%X95{1{GZqWwQGIad5_c@H>cb8p+`Y=cr^?5Ui9T2RcR1ur z7XyrE&xCp8YBJ7Y!1f+Z7Y2XcsET_n=pxZYYNPG|XW$H+8MJ@$W_NbT-+B&&>y9y8 zA!X|45aI-JQmvI87|w>EH$Lw1zEZ5R5Ahqh17EbvcT15WP7o(OVCR(IwJikTtR&99 zM5dNMZ}0EkT}P^x zF-WxZ$662~)Dh|kb^Q!=uEs7|3^L9K1a`ubcEZ>}X7K+~Wk(>3`Zk-9;qiboaMp%v zq$bW-43c+%iEqTWHV!a217|gG#$u3fyMZ)ykW~AC#UNf>3tFp5o`shhdlB{`>_u_m zhrKA>Kbl_&tGFE1%I!Wf;#vC0t&;#P1&LDYM|MJ-F%ZZ=pk6-Y$Kjev5vx4q<_m`j zt?_v#7K3Iz35jGTbT#xFmD!(Oq`IQ z6VEm~xArLWC|nIxgEKlRdPxM%9-maeRtxTJLZbboKY5e9x&GP=&YJMEHeN_`fGDIZ zi0L*_mX0bNRlT^<`ec3RKx9}w0<_JUuf?n=a42vna02osd6T>u=i7E|HLnV6g7(Rc zJr3+~V2{J?_S>a~19^O?14L=jgcwxR9Wuu=uW%cq`RhJ?{j>AkZ`px7ob?V;Gmp+ zB?lmKlyCc>gGWkh7WB;U5Yc(@pCDdw8wi|HaO%k-MMhHY2%zlharhbhOh-yi7SX*w zmHMaP44i>8WzwL7=+zS?ddANpdKUkIpS`)bp&?-HqZ~lqjIOiq(txB@uaRQ8zr}L@ zgYi?u$4P@6RtT->rY$Y`h=6&E}){n6O-xp7x6F0aTUVRU2|6t_*Mn&fJuu z@y+y!Xo^vozHL;|${E+ReWDk@l^T;oa3VMn+)V@*!oWTai=d_sag&U6v++ke0{lZn zfSY&qQzSKCB%P^L#h<3hU)g5*`Gx(Lp^jm1)c;g>Ja%T2-rv4bJ+Gvydgt9>UKkAvcOV#34~q zn@W#|7LfzPz90$iR@Cv1Ie5op)$XOT=H%?1sf_3f&Zyi})>MZR zMlGYEb+PNBay!pme<^jdr)>-&Xi_^i7bqRBw2y^l6KxtH{ZFHbK7(jW@ zJ=!MP{VviJBcyFvE2!6~*96}$ZuXO429#jfDXH}*VazaQ2DMIN17oKoyZ+QNI|TLU z42nNaJk}Hf6ao|iv6zvmy^q+^?8SVOJym{nbflSqO9GI17VIt+kD@_on7m2eWCpJ9 zGjQ-TnHY#?Lyu?dlswR*vh0aNGW0{TKVilc4O2AVj@;5H>aL@6%gQFHT7^f-V$51c z%v$V}jMR?2`FL<Gv3eU1cY8$P&Pg|xjaPXO7GVQngKh@hjO!yMb{dn@HmZFzZ)Bk1 zYF~cKPZR@;XU|0We>EBBq~4a_s0>r*jT%>5xWatEfaU}0-R@|A!VRt|a0brW;D6u@ zoYlY?zI|on93aDOQ7OU~)`Bmr@j0aT_u2Zj>xv{eYXN6}Yrr|tz_);)n^l2El%a!f z{UwsC{=B`vx(S{EWPr7|Ur6gXy-Lp)hIo8Q~Zb^Y!hn*FH)XW$H+-EO~KYB-R`r#miLQM;xFDpnM` zy!Eh*@pDOCZS1*>XJ1XLClxo%8G~nk68n?b*BKv7jrl==j<%%e z*d2X5%ybW6rhDSiv7=+x(Y=F%?AWCqGAYt3*EUk9i#p(Ooo`h$_+ zBBW2b?2MUb<6U9pf`f7vf>xT-+9bW6c|IdLFaDDYV!#;%ry-NT&){c8{A{tDcikkH zT)JK?tey>VLeeIok6P3Am~6eQ9Ko?T;L3xwTrVaD9xG8FK5^~h49O+adJVF zcpi9lO_t>B<6&4SCBa`E)zzcBEV^&e+f#t}C1=vGwR&u+c( z)g*7%g}sDE$?Cdf6>pR+j!}UJpaQe9O4Um0c5{d{M7j-D2+sP1GY2v*0O%6{AWoYd zKObctMGptH8lPu+Uuj#GeQ9JUG$H5^blt)4>KKBvk~sSkIXV8ky}x^NAuf*;6*sms zQ)3*@kAMcApX@~nHexw>Geg048?Zf}bDIrFSlEymdV>%^2%1O&3^>P157uj}*NVR6Ub_eJj4Wei9uER^Nda1u z6%n9uya|mNVGv<3zj&e!pbj`)9l&Lv4!mXPA^jgYG57eWA8OVQ-K0ame2DMb6?`2T zp${^``2O`vZ|7aeEoPzJ-_p_*`5pOPzxO(FKb5_;7g|%{p;6v}v(uG#w5@4dH%n@Z zpg^$$uicG##UX=@*qNgV)rM-f!5Ry3*3zb8QI6{K zID3tA90;k5Q)~8SXclXnF7i}Wq}nzt{`@;4zz7({?V(gmiOLAd$qZ*T(|imD7!1O2 zHeD{3edsK@RIW&;E(Y#1<$)uE0jnge4KbNFgoAGtQ_i198#q+0au%EoNEg`V8>kC4 zEJ4-*nmK$U3^1CIf3nOKU0Md)pQ^LL87mhYl(XOrkJMYy{}DQ4+6Dq=>@L(dY^KOy zccI=ZP1CV%ZYj23QE=8AWkJ|L*l@B!6k%gP!bX1eg|3mLs5HS1u-+`%?qmZJI@MO4 z>N+?}46IjgBa)i@Nm^YLF%&TrF^T$@QJ|kN(LAXtjr-4%2pSc(jHR}mMSe2aGN4+O z?i7M*Ij36tSE#zWcSm)MwTL7VSQ`XS{gflW8#n`Jy4)}S# zExF5=3=eXMDfP=iSp$hsiVzW;2u=i7?52+XOi|STiM-9mAMFV64-o-QY%WbMqOby`IMZy=5*0~V88aI3>CqZZpE;xK(~^Fk zu`>EnSj~R_YOufd-I)TIwsSvtkIG;A{XmqjFO*n9A*-5i7#OQ4Lc)GNln7NqS}d zSnQNMJ&}<{W4BV7Qy2|&r4l42h3>%sCPkVW_hXW;A~H|wt^ zmaigGdJGN!lhB1zcXDkpw5Hm}#;d$DO~@nU>B2^Orkg#$;0&DAz}XcGp0a9_Dp#M0 z^8ad*M~ch01NI*^0l|_3H5)H$5}u?h&q`0BYRLo8epzqBip$P(Gw8VU1FG%6Mg=IW*+(b$RuT-;;c|$>mKtn>S(BXY01^gT)JwTi_K8N(a(j9vC zPoBkIG+nI4a0 zTcY3$oPo0d0hJ4i^;oLO{NmHNUhk$k_U$fwMXxcoTUF4w8ZV?3X;(U_bG}5Ui$T6N z@)$S+XW&dZdueo>*Xr0M8zgNsM}v}2Ax$COMq&VG;0&B0*jyvn^ir@n&#QEbN5?U+ zu+^anv9f4eCpa4d&V&HbL1XqWA7Ue_FpM-gGhw5h)4!@hhsO7>UwS(tg)bMJ;md6! zLxVGL2F{W#NwgDbC$^#H^$=&jx%GeBE~Po};X}2nQ0)_1`K2=|nPwM8sq7Af#NV}> z2$pkJQJ0~zqpC=xfC52*pg>TdMz@=FxidWR=-AP*W6$+CrWL#O-lnar73|wZWaE{j zG&wUw_H$|-l-z|A%fTaa!OzaK@rwZz18B4DE_1lTV9=8=XSs@piSkZ%u=|rv9plWA z!Qk^J^e0wHz@vA!%ta1P^mO#})||Xs?tQx!|CjH7f@wRKrrJ zqIE6eKZo6Wa5jVy14iMkJUfiSZ|UqLZA}!-s1x!{+RG3&5H?y9HqbTDHPAKsu4^Q> zsz|>MMGWK;av7ELaWQZ1n#G<%s1{TUs&!&-=}&Bh6uMY9*F+;x9M2*-)@VJZsHN7q zn<8K zcw{EbLxdnUGldp)B?Dc|{&X=9l|7Sx#et>5lUme~s6p{f@vRf(NUhlkvEtMCFBC;C zrWA?b;Oy;=+(zdtx&LXi@kcuX{6j>56IWxC3n{@JxI4t15oekmLXMf2NDq@)a4Y_K zG%(a9%#DMoA1wSZoXS_HXwt4#VW=~^yq&{8$0?j~5c-&cnxq@N+0)4ia&sM8xI9n}3p=5|()1OR&>B^t`;zRBa zHsN!!Fgz5uWyay_GnW~*w@-KZj3GfFK_m;WOehsujzY=Vy1g!uhr^iHPtMT6cor__ z%V4o=J&l!P23D^6`pf{F>0WG!36+D&JRVv^sj0ozgVZyh$amh@O!xNI#PN>pm)b-I zsVTJ7SB`dnp7|?R}()CQHV%Edr+^tBJvfcD^u&gzut?8gUJ0_NXY6@xT^K~;3YvqMnXnHK5Gei>?u99 z%n)*6^tx)!Yct7Ad)b6$M&vRNt)X&x>@nM;5XlPt4XZmVE zr_b1N`OzD(-^5YwJcQcM$u9YyKRn#5pTE8QkAMB|r<=$1^Y>qGukT)8->sj2c=)=# zj{bi)Putt;_49Q5{J+2ZHhs8XKmY#ezt_+I{r>i8{ruhb=D%*X+nc}3uljKP@Q<6r z>hC~ali2@ua~Z!3@e=;+A{9f#|GfRGX|>_a=${^MzTezG75zgYD(yA8_x>W)an&l( zW=myTce8oe20!2aRs2$)AOHT#L)28r?n8{f&)$fH;Tt7qHYUC50LM`yVu72@MlAA= zzxh+VJ!|jR9r(}7yoU~+_Sh2xLcM>3Y|}zRjEzjXKLFGKV|yEc&;h=QCPe4MW%VlJ z^(^~bCzU}_R#cqgNIzj9nfQOD+3j@ib=$nDR_R2J!~_s$<0Kt7r#W_8)wnudABl%? zB$yu7c^}deZu{-hvMBmn14OEUZy^gnrD8u5b;#8uN}4n?N2C3++S!InQxG@30S^2* z>TN#KsC9v}GTo8^_H2Fn*(Di!Fqo~e8Jy9~Oo6el zde}c`rLqc??O4RQhA*0#`ye4rcANHR$XtFNAWfD_lQc7<&Wb9b95XVSnL2wjl-o2j zPu^Y;B7$DgIB53J%zSf^(hqxnYE=m67A^a4ZJ@sApGjl+)B(O}W~Q~6*ivi=^HCX} zy_q`ZbXZM_nue`EPnwz9W?3sN53-rrcJs8WKkq+3?%kR-h)7q9XPm3V)*JVkaCZtu VE`XYNwvXju!@a4R=`|;H{eO`lLx2DP literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-13433 b/test/fuzz/fuzz-network-parser/oss-fuzz-13433 new file mode 100644 index 0000000000000000000000000000000000000000..1aef8b7d83bd93df5dcc191efabeee598eb8ec7a GIT binary patch literal 29 ccma#{OD!qSFUpSP3XAY@^s_Ci$YFr90GG1~?EnA( literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-network-parser/oss-fuzz-13888 b/test/fuzz/fuzz-network-parser/oss-fuzz-13888 new file mode 100644 index 0000000000000000000000000000000000000000..c75fcb4e8a932562412ddb05cc4349bf596cc6cb GIT binary patch literal 56 zcmXRaOXZ68O)N>yh-F~*vE}l}FD|iFwB-uREGj8Y%*m`uEXmBzvxQ1PrF|Wp0slA= A@c;k- literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-nspawn-oci/basic.json b/test/fuzz/fuzz-nspawn-oci/basic.json new file mode 100644 index 000000000..f42739e03 --- /dev/null +++ b/test/fuzz/fuzz-nspawn-oci/basic.json @@ -0,0 +1,141 @@ +{ + "ociVersion": "1.0.0", + + "root": { + "path": "rootfs", + "readonly": true + }, + + "process": { + "terminal": false, + "consoleSize": { + "height":6667, + "width":6668 + }, + + "user": { + "uid": 14, + "gid": 14, + "additionalGids": [59, 81] + }, + + "args": [ + "/tmp/verify.sh" + ], + + "env": [ + "FOO=BAR", + "WITHSPACES=FOO BAR", + "WITHSHELLCHARS=$ASDF \\\"asdf asdf\\\" !", + "WITHCONTROLCHARS=\\123\\125\\010\\020", + "TERM=xterm" + ], + + "cwd": "/tmp/src", + + "rlimits": [ + { + "type": "RLIMIT_NOFILE", + "hard": 1020, + "soft": 1020 + } + ] + }, + + "mounts": [ + { + "destination": "/tmp/src", + "source": "src", + "options": ["ro"] + }, + + { + "destination": "/tmp/verify.sh", + "source": "verify.sh", + "options": ["ro"] + }, + + { + "destination": "/proc", + "type": "proc", + "source": "proc" + }, + { + "destination": "/dev", + "type": "tmpfs", + "source": "tmpfs", + "options": [ + "mode=777" + ] + }, + { + "destination": "/dev/pts", + "type": "devpts", + "source": "devpts", + "options": [ + "mode=777" + ] + }, + { + "destination": "/dev/shm", + "type": "tmpfs", + "source": "shm", + "options": [ + "mode=777" + ] + }, + { + "destination": "/dev/mqueue", + "type": "mqueue", + "source": "mqueue", + "options": [ + "mode=777" + ] + }, + { + "destination": "/sys", + "type": "sysfs", + "source": "sysfs", + "options": [ + "mode=777" + ] + }, + { + "destination": "/sys/fs/cgroup", + "type": "cgroup", + "source": "cgroup", + "options": [ + "mode=777" + ] + } + ], + + "hooks": {}, + + "linux": { + "resources": { + "devices": [ + { + "allow": false, + "access": "rwm" + } + ] + }, + "namespaces": [ + { + "type": "pid" + }, + { + "type": "ipc" + }, + { + "type": "mount" + } + ] + }, + + "annotations": { + "com.example.key1": "value1", + "com.example.key2": "value2" + } +} diff --git a/test/fuzz/fuzz-nspawn-oci/crash-bffbd2085d4e95c47e9749b3f4a2dbc0580c20d3 b/test/fuzz/fuzz-nspawn-oci/crash-bffbd2085d4e95c47e9749b3f4a2dbc0580c20d3 new file mode 100644 index 000000000..22e42d3ba --- /dev/null +++ b/test/fuzz/fuzz-nspawn-oci/crash-bffbd2085d4e95c47e9749b3f4a2dbc0580c20d3 @@ -0,0 +1,5 @@ +{"ociVersion": "1.0.0", +"linux": {"devices": [ { "access": "mmmw;r"} +] }, "e": "}e" + } + \ No newline at end of file diff --git a/test/fuzz/fuzz-nspawn-oci/crash-db0595479ee2e625fa5419a821009b5eb4d809b7 b/test/fuzz/fuzz-nspawn-oci/crash-db0595479ee2e625fa5419a821009b5eb4d809b7 new file mode 100644 index 000000000..0bf017ce6 --- /dev/null +++ b/test/fuzz/fuzz-nspawn-oci/crash-db0595479ee2e625fa5419a821009b5eb4d809b7 @@ -0,0 +1,92 @@ +{ + "ociVersion": "1.0.0", + + "root": { + "path": "rootfs", + "readonly": true + }, + + "process": { + "terminal": false, + "consoleSize": { + "height":6667, + "width":6668 + }, + + "user": { + "uid": 14, + "gid": 14, + "additionalGids": [59, 81] + }, + + "args": [ + "/tmp/verify.sh" + ], + + "env": [ + "FOO=BAR", + "WITHSPACES=FOO BAR", + "WITHSHELLCHARS=$ASDF \\\"asdf asdf\\\" !", + "WITHCONTROLCHARS=\\123\\125\\010\\020", + "TERM=xterm" + ], + + "cwd": "/tmp/src", + + "rlimits": [ + { + "type": "RLIMIT_NOFILE", + "hard": 1020, + "soft": 1020 + } + ] + }, + + "mounts": [ + { + "destination": "/tmp/src" }, + { + "source": "sysfs", + "options": [ + "mode=777" + ] + }, + { + "destination": "/sys/fs/cgroup", + "type": "cgroup", + "source": "cgroup", + "options": [ + "mode=777" + ] + } + ], + + "hooks": {}, + + "linux": { + "resources": { + "devices": [ + { + "allow": false, + "access": "rwm" + } + ] + }, + "namespaces": [ + { + "type": "pid" + }, + { + "type": "ipc" + }, + { + "type": "mount" + } + ] + }, + + "annotations": { + "com.example.key1": "value1", + "com.example.key2": "value2" + } +} diff --git a/test/fuzz/fuzz-nspawn-settings/basic-config b/test/fuzz/fuzz-nspawn-settings/basic-config new file mode 100644 index 000000000..be0d4e7c7 --- /dev/null +++ b/test/fuzz/fuzz-nspawn-settings/basic-config @@ -0,0 +1,36 @@ +[Exec] +Boot=off +ProcessTwo=off +Parameters=/sbin/init -x=1 +Environment=THIS=that +User=user +WorkingDirectory=/cwd +PivotRoot=/newroot +Capability=CAP_NET +DropCapability=CAP_ADMIN +KillSignal=SIGTERM +Personality=shy +MachineID=edbfea3309ba41ea83e2318c58a8d498 +PrivateUser=1:2 +NotifyReady=no +SystemCallFilters=write + +[Files] +ReadOnly=no +Volatile=no +Bind=/bindthis +BindReadOnly=/bindthisro +TemporaryFileSystem=/thisismytmpfs:rw +Overlay=/thisisanoverlay:/thisisanoverlaytoo +PrivateUsersChown=no + +[Network] +Private=off +VirtualEthernet=yes +VirtualEthernetExtra=veth1:veth2 +Interface=eth1 enp0s1 +MacVLAN=eno1 eno2 +IPVLAN=eno3 enp2s124 +Bridge=bridge123 bridge125 +Zone=myzone +Port=1234 156 -1 diff --git a/test/fuzz/fuzz-nspawn-settings/leak-4ff0e2498f596a77ea68d185c61e9e9ff9bb657f b/test/fuzz/fuzz-nspawn-settings/leak-4ff0e2498f596a77ea68d185c61e9e9ff9bb657f new file mode 100644 index 0000000000000000000000000000000000000000..7be2d2c9da3b551d29207ecb280af51bd3585c8b GIT binary patch literal 133 zcmWlRF$%*l5CmOclb_fxP~$+_n2@9~E{^z&4-_rv4h#R@Qn#}VGY@?~EHnZhux&JD z{KV{C(C{{K=D$2Nx?~F_s|(RwS!|(~@cK;g3<>l_WUqJIqoy@Lal*kl^65aW Q>%lG`tF`-%lA2C*0S4JFU;qFB literal 0 HcmV?d00001 diff --git a/test/fuzz/fuzz-nspawn-settings/oss-fuzz-13691 b/test/fuzz/fuzz-nspawn-settings/oss-fuzz-13691 new file mode 100644 index 000000000..51f46e373 --- /dev/null +++ b/test/fuzz/fuzz-nspawn-settings/oss-fuzz-13691 @@ -0,0 +1,2 @@ +[Files] +Overlay=/:: \ No newline at end of file diff --git a/test/fuzz/fuzz-udev-database/sample.txt b/test/fuzz/fuzz-udev-database/sample.txt new file mode 100644 index 000000000..041c61886 --- /dev/null +++ b/test/fuzz/fuzz-udev-database/sample.txt @@ -0,0 +1,500 @@ +E:ID_ATA=1 +E:ID_ATA_DOWNLOAD_MICROCODE=1 +E:ID_ATA_FEATURE_SET_HPA=1 +E:ID_ATA_FEATURE_SET_HPA_ENABLED=1 +E:ID_ATA_FEATURE_SET_PM=1 +E:ID_ATA_FEATURE_SET_PM_ENABLED=1 +E:ID_ATA_FEATURE_SET_SECURITY=1 +E:ID_ATA_FEATURE_SET_SECURITY_ENABLED=0 +E:ID_ATA_FEATURE_SET_SECURITY_ENHANCED_ERASE_UNIT_MIN=8 +E:ID_ATA_FEATURE_SET_SECURITY_ERASE_UNIT_MIN=2 +E:ID_ATA_FEATURE_SET_SECURITY_FROZEN=1 +E:ID_ATA_FEATURE_SET_SMART=1 +E:ID_ATA_FEATURE_SET_SMART_ENABLED=1 +E:ID_ATA_ROTATION_RATE_RPM=0 +E:ID_ATA_SATA=1 +E:ID_ATA_SATA_SIGNAL_RATE_GEN1=1 +E:ID_ATA_SATA_SIGNAL_RATE_GEN2=1 +E:ID_ATA_WRITE_CACHE=1 +E:ID_ATA_WRITE_CACHE_ENABLED=1 +E:ID_BTRFS_READY=1 +E:ID_BUS=ata +E:ID_BUS=i8042 +E:ID_BUS=pci +E:ID_BUS=usb +E:ID_FOR_SEAT=drm-pci-0000_00_02_0 +E:ID_FOR_SEAT=graphics-pci-0000_00_02_0 +E:ID_FOR_SEAT=input-acpi-LNXVIDEO_00 +E:ID_FOR_SEAT=input-acpi-PNP0C0C_00 +E:ID_FOR_SEAT=input-acpi-PNP0C0D_00 +E:ID_FOR_SEAT=input-acpi-SNY5001_00 +E:ID_FOR_SEAT=input-pci-0000_00_03_0 +E:ID_FOR_SEAT=input-pci-0000_00_14_0-usb-0_5_1_0 +E:ID_FOR_SEAT=input-pci-0000_00_1b_0 +E:ID_FOR_SEAT=input-platform-i8042-serio-0 +E:ID_FOR_SEAT=input-platform-i8042-serio-1 +E:ID_FOR_SEAT=sound-pci-0000_00_03_0 +E:ID_FOR_SEAT=sound-pci-0000_00_1b_0 +E:ID_FOR_SEAT=usb-pci-0000_00_14_0 +E:ID_FOR_SEAT=usb-pci-0000_00_1d_0 +E:ID_FOR_SEAT=usb-pci-0000_00_1d_0-usb-0_1 +E:ID_FOR_SEAT=video4linux-pci-0000_00_14_0-usb-0_5_1_0 +E:ID_FS_LABEL_ENC=回復 +E:ID_FS_LABEL=回復 +E:ID_FS_TYPE= +E:ID_FS_TYPE=btrfs +E:ID_FS_TYPE=ext4 +E:ID_FS_TYPE=ntfs +E:ID_FS_TYPE=swap +E:ID_FS_TYPE=vfat +E:ID_FS_USAGE=filesystem +E:ID_FS_USAGE=other +E:ID_FS_UUID=0f12f260-d308-49b4-9a9f-465a749937ce +E:ID_FS_UUID=23451e06-2ec4-4ae0-86b1-36602653dd78 +E:ID_FS_UUID=5C1A-DCBA +E:ID_FS_UUID=64A41A20A419F570 +E:ID_FS_UUID=9C12322A123209B2 +E:ID_FS_UUID=b95e2a35-09fd-493f-9be7-5616eb9ffc19 +E:ID_FS_UUID=beeb37c6-d4e2-494f-b329-3eb27452e469 +E:ID_FS_UUID=D60878D80878B8D7 +E:ID_FS_UUID_ENC=0f12f260-d308-49b4-9a9f-465a749937ce +E:ID_FS_UUID_ENC=23451e06-2ec4-4ae0-86b1-36602653dd78 +E:ID_FS_UUID_ENC=5C1A-DCBA +E:ID_FS_UUID_ENC=64A41A20A419F570 +E:ID_FS_UUID_ENC=9C12322A123209B2 +E:ID_FS_UUID_ENC=b95e2a35-09fd-493f-9be7-5616eb9ffc19 +E:ID_FS_UUID_ENC=beeb37c6-d4e2-494f-b329-3eb27452e469 +E:ID_FS_UUID_ENC=D60878D80878B8D7 +E:ID_FS_UUID_SUB=7f4127d5-2d4f-48a4-81ff-ddd959f2d653 +E:ID_FS_UUID_SUB_ENC=7f4127d5-2d4f-48a4-81ff-ddd959f2d653 +E:ID_FS_VERSION=1 +E:ID_FS_VERSION=1.0 +E:ID_FS_VERSION=FAT32 +E:ID_INPUT=1 +E:ID_INPUT_HEIGHT_MM=52 +E:ID_INPUT_JOYSTICK=1 +E:ID_INPUT_KEY=1 +E:ID_INPUT_KEYBOARD=1 +E:ID_INPUT_MOUSE=1 +E:ID_INPUT_SWITCH=1 +E:ID_INPUT_TOUCHPAD=1 +E:ID_INPUT_TOUCHPAD_INTEGRATION=internal +E:ID_INPUT_WIDTH_MM=88 +E:ID_MODEL=07dc +E:ID_MODEL=8000 +E:ID_MODEL=EHCI_Host_Controller +E:ID_MODEL_ENC=07dc +E:ID_MODEL_ENC=8000 +E:ID_MODEL_ENC=EHCI\x20Host\x20Controller +E:ID_MODEL_ENC=Nexus\x205X +E:ID_MODEL_ENC=SAMSUNG\x20MZNTD256HAGL-00000\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 +E:ID_MODEL_ENC=USB\x202.0\x20Camera +E:ID_MODEL_ENC=xHCI\x20Host\x20Controller +E:ID_MODEL_FROM_DATABASE=2.0 root hub +E:ID_MODEL_FROM_DATABASE=3.0 root hub +E:ID_MODEL_FROM_DATABASE=8 Series HD Audio Controller +E:ID_MODEL_FROM_DATABASE=8 Series HECI +E:ID_MODEL_FROM_DATABASE=8 Series LPC Controller +E:ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 3 +E:ID_MODEL_FROM_DATABASE=8 Series PCI Express Root Port 4 +E:ID_MODEL_FROM_DATABASE=8 Series SATA Controller 1 [AHCI mode] +E:ID_MODEL_FROM_DATABASE=8 Series SMBus Controller +E:ID_MODEL_FROM_DATABASE=8 Series USB EHCI +E:ID_MODEL_FROM_DATABASE=8 Series USB xHCI HC +E:ID_MODEL_FROM_DATABASE=Haswell-ULT DRAM Controller +E:ID_MODEL_FROM_DATABASE=Haswell-ULT HD Audio Controller +E:ID_MODEL_FROM_DATABASE=Haswell-ULT Integrated Graphics Controller +E:ID_MODEL_FROM_DATABASE=Nexus Device (MTP) +E:ID_MODEL_FROM_DATABASE=Wireless 7260 (Dual Band Wireless-N 7260) +E:ID_MODEL_ID=0002 +E:ID_MODEL_ID=0003 +E:ID_MODEL_ID=07dc +E:ID_MODEL_ID=0x08b1 +E:ID_MODEL_ID=0x0a0c +E:ID_MODEL_ID=0x9c20 +E:ID_MODEL_ID=4ee1 +E:ID_MODEL_ID=8000 +E:ID_MODEL_ID=b3be +E:ID_MODEL=Nexus_5X +E:ID_MODEL=SAMSUNG_MZNTD256HAGL-00000 +E:ID_MODEL=USB_2.0_Camera +E:ID_MODEL=xHCI_Host_Controller +E:ID_NET_DRIVER=bridge +E:ID_NET_DRIVER=iwlwifi +E:ID_NET_LINK_FILE=/usr/lib/systemd/network/99-default.link +E:ID_NET_NAME_MAC=wlx00123456abcd +E:ID_NET_NAME_MAC=wlx5c514f1383eb +E:ID_NET_NAME_PATH=wlp1s0 +E:ID_NET_NAME=wlp1s0 +E:ID_NET_NAMING_SCHEME=v240 +E:ID_OUI_FROM_DATABASE=Intel Corporate +E:ID_PART_ENTRY_DISK=8:0 +E:ID_PART_ENTRY_FLAGS=0x1 +E:ID_PART_ENTRY_FLAGS=0x8000000000000001 +E:ID_PART_ENTRY_NAME=Basic\x20data\x20partition +E:ID_PART_ENTRY_NAME=EFI\x20System\x20Partition +E:ID_PART_ENTRY_NAME=Microsoft\x20reserved\x20partition +E:ID_PART_ENTRY_NUMBER=1 +E:ID_PART_ENTRY_NUMBER=2 +E:ID_PART_ENTRY_NUMBER=3 +E:ID_PART_ENTRY_NUMBER=4 +E:ID_PART_ENTRY_NUMBER=5 +E:ID_PART_ENTRY_NUMBER=6 +E:ID_PART_ENTRY_NUMBER=7 +E:ID_PART_ENTRY_NUMBER=8 +E:ID_PART_ENTRY_NUMBER=9 +E:ID_PART_ENTRY_OFFSET=1128448 +E:ID_PART_ENTRY_OFFSET=1161216 +E:ID_PART_ENTRY_OFFSET=132481024 +E:ID_PART_ENTRY_OFFSET=134219776 +E:ID_PART_ENTRY_OFFSET=135243776 +E:ID_PART_ENTRY_OFFSET=143372288 +E:ID_PART_ENTRY_OFFSET=2048 +E:ID_PART_ENTRY_OFFSET=248229888 +E:ID_PART_ENTRY_OFFSET=923648 +E:ID_PART_ENTRY_SCHEME=gpt +E:ID_PART_ENTRY_SIZE=1024000 +E:ID_PART_ENTRY_SIZE=104857600 +E:ID_PART_ENTRY_SIZE=131319561 +E:ID_PART_ENTRY_SIZE=1738752 +E:ID_PART_ENTRY_SIZE=204800 +E:ID_PART_ENTRY_SIZE=251887616 +E:ID_PART_ENTRY_SIZE=32768 +E:ID_PART_ENTRY_SIZE=8128512 +E:ID_PART_ENTRY_SIZE=921600 +E:ID_PART_ENTRY_TYPE=0657fd6d-a4ab-43c4-84e5-0933c84b4f4f +E:ID_PART_ENTRY_TYPE=0fc63daf-8483-4772-8e79-3d69d8477de4 +E:ID_PART_ENTRY_TYPE=c12a7328-f81f-11d2-ba4b-00a0c93ec93b +E:ID_PART_ENTRY_TYPE=de94bba4-06d1-4d40-a16a-bfd50179d6ac +E:ID_PART_ENTRY_TYPE=e3c9e316-0b5c-4db8-817d-f92df00215ae +E:ID_PART_ENTRY_TYPE=ebd0a0a2-b9e5-4433-87c0-68b6b72699c7 +E:ID_PART_ENTRY_UUID=1d655759-7350-4d1e-b679-ae54e599c796 +E:ID_PART_ENTRY_UUID=55202025-f2e2-4b92-9b4b-7fa16d804a35 +E:ID_PART_ENTRY_UUID=611ffa59-e1da-4e0c-8b7e-83dd61edf945 +E:ID_PART_ENTRY_UUID=9e241958-b22d-4813-8504-791d45cbf0bc +E:ID_PART_ENTRY_UUID=9ee3d84f-122d-4d5c-b62b-1006defbcbc1 +E:ID_PART_ENTRY_UUID=a4b0949c-5d61-499b-b336-592ef4e0d423 +E:ID_PART_ENTRY_UUID=b092dc67-d577-4c77-84c4-db681a69a200 +E:ID_PART_ENTRY_UUID=cc77a315-4305-4982-88e6-b52721460ce7 +E:ID_PART_ENTRY_UUID=f17bf199-921b-4cbd-bcbf-15b75ec80b21 +E:ID_PART_TABLE_TYPE=gpt +E:ID_PART_TABLE_UUID=130de398-eb17-4bac-8a19-fe2e64163e91 +E:ID_PATH=acpi-LNXVIDEO:00 +E:ID_PATH=acpi-PNP0C0C:00 +E:ID_PATH=acpi-PNP0C0D:00 +E:ID_PATH=acpi-SNY5001:00 +E:ID_PATH=pci-0000:00:02.0 +E:ID_PATH=pci-0000:00:03.0 +E:ID_PATH=pci-0000:00:14.0 +E:ID_PATH=pci-0000:00:14.0-usb-0:5:1.0 +E:ID_PATH=pci-0000:00:14.0-usb-0:6:1.0 +E:ID_PATH=pci-0000:00:1b.0 +E:ID_PATH=pci-0000:00:1d.0 +E:ID_PATH=pci-0000:00:1d.0-usb-0:1 +E:ID_PATH=pci-0000:00:1f.2-ata-4 +E:ID_PATH=pci-0000:01:00.0 +E:ID_PATH=platform-i8042-serio-0 +E:ID_PATH=platform-i8042-serio-1 +E:ID_PATH_TAG=acpi-LNXVIDEO_00 +E:ID_PATH_TAG=acpi-PNP0C0C_00 +E:ID_PATH_TAG=acpi-PNP0C0D_00 +E:ID_PATH_TAG=acpi-SNY5001_00 +E:ID_PATH_TAG=pci-0000_00_02_0 +E:ID_PATH_TAG=pci-0000_00_03_0 +E:ID_PATH_TAG=pci-0000_00_14_0 +E:ID_PATH_TAG=pci-0000_00_14_0-usb-0_5_1_0 +E:ID_PATH_TAG=pci-0000_00_14_0-usb-0_6_1_0 +E:ID_PATH_TAG=pci-0000_00_1b_0 +E:ID_PATH_TAG=pci-0000_00_1d_0 +E:ID_PATH_TAG=pci-0000_00_1d_0-usb-0_1 +E:ID_PATH_TAG=pci-0000_00_1f_2-ata-4 +E:ID_PATH_TAG=pci-0000_01_00_0 +E:ID_PATH_TAG=platform-i8042-serio-0 +E:ID_PATH_TAG=platform-i8042-serio-1 +E:ID_PCI_CLASS_FROM_DATABASE=Bridge +E:ID_PCI_CLASS_FROM_DATABASE=Communication controller +E:ID_PCI_CLASS_FROM_DATABASE=Display controller +E:ID_PCI_CLASS_FROM_DATABASE=Mass storage controller +E:ID_PCI_CLASS_FROM_DATABASE=Multimedia controller +E:ID_PCI_CLASS_FROM_DATABASE=Network controller +E:ID_PCI_CLASS_FROM_DATABASE=Serial bus controller +E:ID_PCI_INTERFACE_FROM_DATABASE=AHCI 1.0 +E:ID_PCI_INTERFACE_FROM_DATABASE=EHCI +E:ID_PCI_INTERFACE_FROM_DATABASE=Normal decode +E:ID_PCI_INTERFACE_FROM_DATABASE=VGA controller +E:ID_PCI_INTERFACE_FROM_DATABASE=XHCI +E:ID_PCI_SUBCLASS_FROM_DATABASE=Audio device +E:ID_PCI_SUBCLASS_FROM_DATABASE=Communication controller +E:ID_PCI_SUBCLASS_FROM_DATABASE=Host bridge +E:ID_PCI_SUBCLASS_FROM_DATABASE=ISA bridge +E:ID_PCI_SUBCLASS_FROM_DATABASE=Network controller +E:ID_PCI_SUBCLASS_FROM_DATABASE=PCI bridge +E:ID_PCI_SUBCLASS_FROM_DATABASE=SATA controller +E:ID_PCI_SUBCLASS_FROM_DATABASE=SMBus +E:ID_PCI_SUBCLASS_FROM_DATABASE=USB controller +E:ID_PCI_SUBCLASS_FROM_DATABASE=VGA compatible controller +E:ID_REVISION=0001 +E:ID_REVISION=0004 +E:ID_REVISION=0310 +E:ID_REVISION=0420 +E:ID_REVISION=5811 +E:ID_REVISION=DXT2300Q +E:ID_SERIAL=8087_07dc +E:ID_SERIAL=8087_8000 +E:ID_SERIAL=Chicony_Electronics_Co._Ltd._USB_2.0_Camera_0x0001 +E:ID_SERIAL=LGE_Nexus_5X_026442e0677ccae8 +E:ID_SERIAL=Linux_4.20.11-200.fc29.x86_64_ehci_hcd_EHCI_Host_Controller_0000:00:1d.0 +E:ID_SERIAL=Linux_4.20.11-200.fc29.x86_64_xhci-hcd_xHCI_Host_Controller_0000:00:14.0 +E:ID_SERIAL=noserial +E:ID_SERIAL=SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616 +E:ID_SERIAL_SHORT=0000:00:14.0 +E:ID_SERIAL_SHORT=0000:00:1d.0 +E:ID_SERIAL_SHORT=026442e0677ccae8 +E:ID_SERIAL_SHORT=0x0001 +E:ID_SERIAL_SHORT=S15ZNYAD408616 +E:ID_TYPE=disk +E:ID_TYPE=video +E:ID_USB_CLASS_FROM_DATABASE=Hub +E:ID_USB_CLASS_FROM_DATABASE=Miscellaneous Device +E:ID_USB_CLASS_FROM_DATABASE=Wireless +E:ID_USB_DRIVER=uvcvideo +E:ID_USB_INTERFACE_NUM=00 +E:ID_USB_INTERFACES=:090000: +E:ID_USB_INTERFACES=:0e0100:0e0200: +E:ID_USB_INTERFACES=:e00101: +E:ID_USB_INTERFACES=:ffff00: +E:ID_USB_PROTOCOL_FROM_DATABASE=Bluetooth +E:ID_USB_PROTOCOL_FROM_DATABASE=Full speed (or root) hub +E:ID_USB_PROTOCOL_FROM_DATABASE=Interface Association +E:ID_USB_PROTOCOL_FROM_DATABASE=Single TT +E:ID_USB_SUBCLASS_FROM_DATABASE=Radio Frequency +E:ID_V4L_CAPABILITIES=:capture: +E:ID_V4L_PRODUCT=USB 2.0 Camera: USB 2.0 Camera +E:ID_V4L_VERSION=2 +E:ID_VENDOR=8087 +E:ID_VENDOR=Chicony_Electronics_Co._Ltd. +E:ID_VENDOR_ENC=8087 +E:ID_VENDOR_ENC=Chicony\x20Electronics\x20Co.\x2cLtd. +E:ID_VENDOR_ENC=LGE +E:ID_VENDOR_ENC=Linux\x204.20.11-200.fc29.x86_64\x20ehci_hcd +E:ID_VENDOR_ENC=Linux\x204.20.11-200.fc29.x86_64\x20xhci-hcd +E:ID_VENDOR_FROM_DATABASE=Chicony Electronics Co., Ltd +E:ID_VENDOR_FROM_DATABASE=Google Inc. +E:ID_VENDOR_FROM_DATABASE=Intel Corp. +E:ID_VENDOR_FROM_DATABASE=Intel Corporation +E:ID_VENDOR_FROM_DATABASE=Linux Foundation +E:ID_VENDOR_FROM_DATABASE=NXP Semiconductors bv. +E:ID_VENDOR_FROM_DATABASE=The Linux Foundation +E:ID_VENDOR_ID=04f2 +E:ID_VENDOR_ID=0x8086 +E:ID_VENDOR_ID=18d1 +E:ID_VENDOR_ID=1d6b +E:ID_VENDOR_ID=8087 +E:ID_VENDOR=LGE +E:ID_VENDOR=Linux_4.20.11-200.fc29.x86_64_ehci_hcd +E:ID_VENDOR=Linux_4.20.11-200.fc29.x86_64_xhci-hcd +E:ID_WWN=0x500253850002aca7 +E:ID_WWN_WITH_EXTENSION=0x500253850002aca7 +E:KEYBOARD_KEY_06=mute +E:KEYBOARD_KEY_07=volumedown +E:KEYBOARD_KEY_08=volumeup +E:KEYBOARD_KEY_09=brightnessdown +E:KEYBOARD_KEY_0a=brightnessup +E:KEYBOARD_KEY_0b=switchvideomode +E:KEYBOARD_KEY_0e=zoom +E:KEYBOARD_KEY_10=suspend +E:LIBINPUT_DEVICE_GROUP= +E:LIBINPUT_DEVICE_GROUP=0/0/0:ALSA +E:LIBINPUT_DEVICE_GROUP=11/1/1:isa0060/serio0 +E:LIBINPUT_DEVICE_GROUP=11/2/7:isa0060/serio1 +E:LIBINPUT_DEVICE_GROUP=19/0/1:PNP0C0C/button +E:LIBINPUT_DEVICE_GROUP=19/0/6:LNXVIDEO/video +E:LIBINPUT_DEVICE_GROUP=3/4f2/b3be:usb-0000:00:14.0-5/button +E:SOUND_FORM_FACTOR=internal +E:SOUND_INITIALIZED=1 +E:SYSTEMD_ALIAS=/sys/subsystem/bluetooth/devices/hci0 +E:SYSTEMD_ALIAS=/sys/subsystem/net/devices/docker0 +E:SYSTEMD_ALIAS=/sys/subsystem/net/devices/wlp1s0 /sys/subsystem/net/devices/wlp1s0 +E:SYSTEMD_RFKILL=1 +E:SYSTEMD_WANTS=bluetooth.target +E:SYSTEMD_WANTS=sound.target +E:SYSTEMD_WANTS=sys-kernel-config.mount +E:SYSTEMD_WANTS=systemd-backlight@backlight:intel_backlight.service +E:SYSTEMD_WANTS=systemd-rfkill.socket +G:master-of-seat +G:power-switch +G:seat +G:systemd +G:uaccess +I:10305698 +I:10395221 +I:10782777 +I:10810535 +I:11197034 +I:11264776 +I:11840274 +I:11863672 +I:11921972 +I:12272821 +I:124311058 +I:124356828 +I:12608364 +I:12651337 +I:12666911 +I:12717449 +I:12737331 +I:12824659 +I:13034589 +I:13446543 +I:16700949 +I:16848532 +I:16854530 +I:16854680 +I:16943840 +I:17041135 +I:17125002 +I:17310738 +I:17346430 +I:17447492 +I:17523623 +I:17918119 +I:17925442 +I:18121723 +I:18140865 +I:18291271 +I:18306009 +I:18313790 +I:18351304 +I:18357657 +I:18398601 +I:18598995 +I:18618482 +I:18712164 +I:18723922 +I:18735577 +I:18740647 +I:18788491 +I:18944489 +I:19057658 +I:19100102 +I:19137360 +I:19562855 +I:19766580 +I:20762981 +I:20781206 +I:20800551 +I:20849301 +I:20875108 +I:20952531 +I:20973791 +I:5594028 +I:6515738 +I:8368250 +I:8459962 +I:8528809 +I:8529967 +I:8586056 +I:8593736 +I:8914299 +I:9003355 +I:9037102 +I:9042481 +I:9057797 +I:9117477 +I:9120365 +I:9208582 +I:9209296 +I:9244279 +I:9250754 +I:9257625 +I:9258871 +I:9317204 +I:9347715 +I:9358092 +I:9380474 +I:9450065 +I:9484613 +I:9495455 +I:9551757 +I:9706931 +I:9749967 +I:9876257 +L:-100 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part1 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part2 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part3 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part4 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part5 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part6 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part7 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part8 +S:disk/by-id/ata-SAMSUNG_MZNTD256HAGL-00000_S15ZNYAD408616-part9 +S:disk/by-id/wwn-0x500253850002aca7 +S:disk/by-id/wwn-0x500253850002aca7-part1 +S:disk/by-id/wwn-0x500253850002aca7-part2 +S:disk/by-id/wwn-0x500253850002aca7-part3 +S:disk/by-id/wwn-0x500253850002aca7-part4 +S:disk/by-id/wwn-0x500253850002aca7-part5 +S:disk/by-id/wwn-0x500253850002aca7-part6 +S:disk/by-id/wwn-0x500253850002aca7-part7 +S:disk/by-id/wwn-0x500253850002aca7-part8 +S:disk/by-id/wwn-0x500253850002aca7-part9 +S:disk/by-label/回復 +S:disk/by-partlabel/Basic\x20data\x20partition +S:disk/by-partlabel/EFI\x20System\x20Partition +S:disk/by-partlabel/Microsoft\x20reserved\x20partition +S:disk/by-partuuid/1d655759-7350-4d1e-b679-ae54e599c796 +S:disk/by-partuuid/55202025-f2e2-4b92-9b4b-7fa16d804a35 +S:disk/by-partuuid/611ffa59-e1da-4e0c-8b7e-83dd61edf945 +S:disk/by-partuuid/9e241958-b22d-4813-8504-791d45cbf0bc +S:disk/by-partuuid/9ee3d84f-122d-4d5c-b62b-1006defbcbc1 +S:disk/by-partuuid/a4b0949c-5d61-499b-b336-592ef4e0d423 +S:disk/by-partuuid/b092dc67-d577-4c77-84c4-db681a69a200 +S:disk/by-partuuid/cc77a315-4305-4982-88e6-b52721460ce7 +S:disk/by-partuuid/f17bf199-921b-4cbd-bcbf-15b75ec80b21 +S:disk/by-path/pci-0000:00:1f.2-ata-4 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part1 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part2 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part3 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part4 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part5 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part6 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part7 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part8 +S:disk/by-path/pci-0000:00:1f.2-ata-4-part9 +S:disk/by-uuid/0f12f260-d308-49b4-9a9f-465a749937ce +S:disk/by-uuid/23451e06-2ec4-4ae0-86b1-36602653dd78 +S:disk/by-uuid/5C1A-DCBA +S:disk/by-uuid/64A41A20A419F570 +S:disk/by-uuid/9C12322A123209B2 +S:disk/by-uuid/b95e2a35-09fd-493f-9be7-5616eb9ffc19 +S:disk/by-uuid/beeb37c6-d4e2-494f-b329-3eb27452e469 +S:disk/by-uuid/D60878D80878B8D7 +S:dri/by-path/pci-0000:00:02.0-card +S:dri/by-path/pci-0000:00:02.0-render +S:input/by-id/usb-Chicony_Electronics_Co._Ltd._USB_2.0_Camera_0x0001-event-if00 +S:input/by-path/acpi-SNY5001:00-event-joystick +S:input/by-path/acpi-SNY5001:00-event-mouse +S:input/by-path/acpi-SNY5001:00-mouse +S:input/by-path/pci-0000:00:14.0-usb-0:5:1.0-event +S:input/by-path/platform-i8042-serio-0-event-kbd +S:input/by-path/platform-i8042-serio-1-event-mouse +S:input/by-path/platform-i8042-serio-1-mouse +S:rtc +S:snd/by-path/pci-0000:00:03.0 +S:snd/by-path/pci-0000:00:1b.0 +S:v4l/by-id/usb-Chicony_Electronics_Co._Ltd._USB_2.0_Camera_0x0001-video-index0 +S:v4l/by-id/usb-Chicony_Electronics_Co._Ltd._USB_2.0_Camera_0x0001-video-index1 +S:v4l/by-path/pci-0000:00:14.0-usb-0:5:1.0-video-index0 +S:v4l/by-path/pci-0000:00:14.0-usb-0:5:1.0-video-index1 +W:1 +W:10 +W:2 +W:3 +W:4 +W:5 +W:6 +W:7 +W:8 +W:9 diff --git a/test/fuzz/fuzz-unit-file/directives.service b/test/fuzz/fuzz-unit-file/directives.service index 4d7526f63..86e59184d 100644 --- a/test/fuzz/fuzz-unit-file/directives.service +++ b/test/fuzz/fuzz-unit-file/directives.service @@ -409,7 +409,7 @@ From= FwMark= GVRP= Gateway= -GatewayOnlink= +GatewayOnLink= GenericReceiveOffload= GenericSegmentationOffload= GratuitousARP= @@ -851,6 +851,7 @@ ReserveVT= RestrictAddressFamilies= RestrictNamespaces= RestrictRealtime= +RestrictSUIDSGID= RuntimeDirectory= RuntimeDirectoryMode= RuntimeDirectoryPreserve= diff --git a/test/fuzz/fuzz-unit-file/oss-fuzz-13125 b/test/fuzz/fuzz-unit-file/oss-fuzz-13125 new file mode 100644 index 000000000..b671e110e --- /dev/null +++ b/test/fuzz/fuzz-unit-file/oss-fuzz-13125 @@ -0,0 +1,10 @@ +timer + . +[Timer] +OnCalendar= CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%%H%H%CCH%L%H%L%H%H%L%H%H%H%H%C%L%HrH%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CL%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%%%H%HHH%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%HL%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%HeH%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%%H%%H%CLH%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%HH%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H.[ H/var/lH%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%HH%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H;C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%HeH%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%HH%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CLANG=C.%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%HeH%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%LH%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%%H%%H%CLH%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%HH%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H;C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%HeH%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%H%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%HeH%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%HeH%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%HeH%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%HH%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH󠁪%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%HeH%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%L%H%H%C%H%H%CH%L%H%L%HH%E%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H4,40,04,4..0,04,04,0,4..0,0,04,4,40,04,4..0,04,04,0,4..0,0,04,4,40,30,04,4..0,0,04,8,40,04,4..0=|w=s utc +[Timer] +OnCalendar=Wed utc + +OnCalendar=s%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%H%CH%L%H%L%HH%H%C%H%H%H%H%C%H%H%CH%S%H%L%H%H%L%H%H%H%H%C%L%H%H%H%H%H%H%C%H%H%CH%L%H%L%H%H%L%H%H%H%H%C%L%H%H%C%H%w=s utc +[Timer] +OnCHalend%CH%L%H%L%HH%H%ar0,4C%H. \ No newline at end of file diff --git a/test/fuzz/fuzz-unit-file/systemd-machined.service b/test/fuzz/fuzz-unit-file/systemd-machined.service index 448f062ec..70b627c5f 100644 --- a/test/fuzz/fuzz-unit-file/systemd-machined.service +++ b/test/fuzz/fuzz-unit-file/systemd-machined.service @@ -15,6 +15,14 @@ Documentation=https://www.freedesktop.org/wiki/Software/systemd/machined Wants=machine.slice After=machine.slice RequiresMountsFor=/var/lib/machines +ConditionNull=true +ConditionNull= +ConditionNull=|!false +OnFailureIsolate=false +FailureActionExitStatus=222 +FailureActionExitStatus= +SuccessActionExitStatus=0 +SuccessActionExitStatus= [Service] ExecStart=/usr/lib/systemd/systemd-machined @@ -32,3 +40,13 @@ IPAddressDeny=any # 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 # "machinectl bind" operation. + +SELinuxContext=system_u:system_r:kernel_t:s0 +AppArmorProfile=profile +SELinuxContext=-system_u:system_r:kernel_t:s22 +AppArmorProfile=-profile +IODeviceLatencyTargetSec=/dev/sda 25ms +IODeviceLatencyTargetSec=/dev/sdb 2h +PIDFile=%t/mypid +PIDFile= +DisableControllers= diff --git a/test/fuzz/meson.build b/test/fuzz/meson.build index daec2ead8..c514f57fe 100644 --- a/test/fuzz/meson.build +++ b/test/fuzz/meson.build @@ -4,7 +4,7 @@ sanitize_address = custom_target( 'sanitize-address-fuzzers', output : 'sanitize-address-fuzzers', command : [meson_build_sh, - meson.source_root(), + project_source_root, '@OUTPUT@', 'fuzzers', '-Db_lundef=false -Db_sanitize=address', @@ -16,11 +16,11 @@ sanitizers = [['address', sanitize_address]] if git.found() out = run_command( git, - '--git-dir=@0@/.git'.format(meson.source_root()), + '--git-dir=@0@/.git'.format(project_source_root), 'ls-files', ':/test/fuzz/*/*') else out = run_command( - 'sh', '-c', 'ls @0@/*/*'.format(meson.current_source_dir())) + 'sh', '-c', 'ls @0@/test/fuzz/*/*'.format(project_source_root)) endif fuzz_regression_tests = [] diff --git a/test/meson.build b/test/meson.build index 94903934a..7255c79dc 100644 --- a/test/meson.build +++ b/test/meson.build @@ -55,6 +55,7 @@ test_data_files = ''' test-execute/exec-dynamicuser-statedir-migrate-step2.service test-execute/exec-dynamicuser-statedir.service test-execute/exec-dynamicuser-supplementarygroups.service + test-execute/exec-environment-no-substitute.service test-execute/exec-environment-empty.service test-execute/exec-environment-multiple.service test-execute/exec-environment.service @@ -66,7 +67,7 @@ test_data_files = ''' test-execute/exec-ignoresigpipe-no.service test-execute/exec-ignoresigpipe-yes.service test-execute/exec-inaccessiblepaths-mount-propagation.service - test-execute/exec-inaccessiblepaths-proc.service + test-execute/exec-inaccessiblepaths-sys.service test-execute/exec-ioschedulingclass-best-effort.service test-execute/exec-ioschedulingclass-idle.service test-execute/exec-ioschedulingclass-none.service @@ -93,6 +94,7 @@ test_data_files = ''' test-execute/exec-privatenetwork-yes.service test-execute/exec-privatetmp-no.service test-execute/exec-privatetmp-yes.service + test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service test-execute/exec-protectkernelmodules-no-capabilities.service test-execute/exec-protectkernelmodules-yes-capabilities.service test-execute/exec-protectkernelmodules-yes-mount-propagation.service diff --git a/test/networkd-test.py b/test/networkd-test.py index 7011abc96..8b1aeeda3 100755 --- a/test/networkd-test.py +++ b/test/networkd-test.py @@ -29,6 +29,7 @@ import time import unittest HAVE_DNSMASQ = shutil.which('dnsmasq') is not None +IS_CONTAINER = subprocess.call(['systemd-detect-virt', '--quiet', '--container']) == 0 NETWORK_UNITDIR = '/run/systemd/network' @@ -282,11 +283,11 @@ class ClientTestBase(NetworkdTestingUtilities): klass.orig_log_level = subprocess.check_output( ['systemctl', 'show', '--value', '--property', 'LogLevel'], universal_newlines=True).strip() - subprocess.check_call(['systemd-analyze', 'set-log-level', 'debug']) + subprocess.check_call(['systemd-analyze', 'log-level', 'debug']) @classmethod def tearDownClass(klass): - subprocess.check_call(['systemd-analyze', 'set-log-level', klass.orig_log_level]) + subprocess.check_call(['systemd-analyze', 'log-level', klass.orig_log_level]) def setUp(self): self.iface = 'test_eth42' @@ -335,13 +336,16 @@ class ClientTestBase(NetworkdTestingUtilities): raise NotImplementedError('must be implemented by a subclass') + def start_unit(self, unit): + try: + subprocess.check_call(['systemctl', 'start', unit]) + except subprocess.CalledProcessError: + self.show_journal(unit) + raise + def do_test(self, coldplug=True, ipv6=False, extra_opts='', online_timeout=10, dhcp_mode='yes'): - try: - subprocess.check_call(['systemctl', 'start', 'systemd-resolved']) - except subprocess.CalledProcessError: - self.show_journal('systemd-resolved.service') - raise + self.start_unit('systemd-resolved') self.write_network(self.config, '''\ [Match] Name={} @@ -352,14 +356,14 @@ DHCP={} if coldplug: # create interface first, then start networkd self.create_iface(ipv6=ipv6) - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-networkd') elif coldplug is not None: # start networkd first, then create interface - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-networkd') self.create_iface(ipv6=ipv6) else: # "None" means test sets up interface by itself - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-networkd') try: subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', @@ -472,12 +476,19 @@ MACAddress=12:34:56:78:9a:bc''') [Match] Name=dummy0 [Network] -Address=192.168.42.100 +Address=192.168.42.100/24 DNS=192.168.42.1 Domains= ~company''') - self.do_test(coldplug=True, ipv6=False, - extra_opts='IPv6AcceptRouterAdvertisements=False') + try: + self.do_test(coldplug=True, ipv6=False, + extra_opts='IPv6AcceptRouterAdvertisements=False') + except subprocess.CalledProcessError as e: + # networkd often fails to start in LXC: https://github.com/systemd/systemd/issues/11848 + if IS_CONTAINER and e.cmd == ['systemctl', 'start', 'systemd-networkd']: + raise unittest.SkipTest('https://github.com/systemd/systemd/issues/11848') + else: + raise with open(RESOLV_CONF) as f: contents = f.read() @@ -496,12 +507,19 @@ MACAddress=12:34:56:78:9a:bc''') self.write_network('myvpn.network', '''[Match] Name=dummy0 [Network] -Address=192.168.42.100 +Address=192.168.42.100/24 DNS=192.168.42.1 Domains= ~company ~.''') - self.do_test(coldplug=True, ipv6=False, - extra_opts='IPv6AcceptRouterAdvertisements=False') + try: + self.do_test(coldplug=True, ipv6=False, + extra_opts='IPv6AcceptRouterAdvertisements=False') + except subprocess.CalledProcessError as e: + # networkd often fails to start in LXC: https://github.com/systemd/systemd/issues/11848 + if IS_CONTAINER and e.cmd == ['systemctl', 'start', 'systemd-networkd']: + raise unittest.SkipTest('https://github.com/systemd/systemd/issues/11848') + else: + raise with open(RESOLV_CONF) as f: contents = f.read() @@ -575,6 +593,13 @@ class DnsmasqClientTest(ClientTestBase, unittest.TestCase): def test_resolved_domain_restricted_dns(self): '''resolved: domain-restricted DNS servers''' + # FIXME: resolvectl query fails with enabled DNSSEC against our dnsmasq + 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\n') + self.addCleanup(os.remove, conf) + # create interface for generic connections; this will map all DNS names # to 192.168.42.1 self.create_iface(dnsmasq_opts=['--address=/#/192.168.42.1']) @@ -611,7 +636,7 @@ Address=10.241.3.2/24 DNS=10.241.3.1 Domains= ~company ~lab''') - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-networkd') subprocess.check_call([NETWORKD_WAIT_ONLINE, '--interface', self.iface, '--interface=testvpnclient', '--timeout=20']) @@ -648,52 +673,52 @@ Domains= ~company ~lab''') '''resolved queries to /etc/hosts''' # FIXME: -t MX query fails with enabled DNSSEC (even when using - # the known negative trust anchor .internal instead of .example) + # the known negative trust anchor .internal instead of .example.com) 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\nLLMNR=no\nMulticastDNS=no\n') self.addCleanup(os.remove, conf) - # create /etc/hosts bind mount which resolves my.example for IPv4 + # create /etc/hosts bind mount which resolves my.example.com for IPv4 hosts = os.path.join(self.workdir, 'hosts') with open(hosts, 'w') as f: - f.write('172.16.99.99 my.example\n') + f.write('172.16.99.99 my.example.com\n') subprocess.check_call(['mount', '--bind', hosts, '/etc/hosts']) self.addCleanup(subprocess.call, ['umount', '/etc/hosts']) subprocess.check_call(['systemctl', 'stop', 'systemd-resolved.service']) # note: different IPv4 address here, so that it's easy to tell apart # what resolved the query - self.create_iface(dnsmasq_opts=['--host-record=my.example,172.16.99.1,2600::99:99', - '--host-record=other.example,172.16.0.42,2600::42', - '--mx-host=example,mail.example'], + self.create_iface(dnsmasq_opts=['--host-record=my.example.com,172.16.99.1,2600::99:99', + '--host-record=other.example.com,172.16.0.42,2600::42', + '--mx-host=example.com,mail.example.com'], ipv6=True) self.do_test(coldplug=None, ipv6=True) try: # family specific queries - out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example']) - self.assertIn(b'my.example: 172.16.99.99', out) + out = subprocess.check_output(['resolvectl', 'query', '-4', 'my.example.com']) + self.assertIn(b'my.example.com: 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(['resolvectl', 'query', '-6', 'my.example']), 0) + self.assertNotEqual(subprocess.call(['resolvectl', 'query', '-6', 'my.example.com']), 0) # "any family" query; IPv4 should come from /etc/hosts - out = subprocess.check_output(['resolvectl', 'query', 'my.example']) - self.assertIn(b'my.example: 172.16.99.99', out) + out = subprocess.check_output(['resolvectl', 'query', 'my.example.com']) + self.assertIn(b'my.example.com: 172.16.99.99', out) # IP → name lookup; again, takes the /etc/hosts one out = subprocess.check_output(['resolvectl', 'query', '172.16.99.99']) - self.assertIn(b'172.16.99.99: my.example', out) + self.assertIn(b'172.16.99.99: my.example.com', out) # non-address RRs should fall back to DNS - out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example']) - self.assertIn(b'example IN MX 1 mail.example', out) + out = subprocess.check_output(['resolvectl', 'query', '--type=MX', 'example.com']) + self.assertIn(b'example.com IN MX 1 mail.example.com', out) # other domains query DNS - out = subprocess.check_output(['resolvectl', 'query', 'other.example']) + out = subprocess.check_output(['resolvectl', 'query', 'other.example.com']) self.assertIn(b'172.16.0.42', out) out = subprocess.check_output(['resolvectl', 'query', '172.16.0.42']) - self.assertIn(b'172.16.0.42: other.example', out) + self.assertIn(b'172.16.0.42: other.example.com', out) except (AssertionError, subprocess.CalledProcessError): self.show_journal('systemd-resolved.service') self.print_server_log() @@ -877,11 +902,11 @@ MACAddress=12:34:56:78:9a:bc''') [Match] Name=dummy0 [Network] -Address=192.168.42.100 +Address=192.168.42.100/24 DNS=192.168.42.1 Domains= one two three four five six seven eight nine ten''') - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-networkd') for timeout in range(50): with open(RESOLV_CONF) as f: @@ -909,11 +934,11 @@ MACAddress=12:34:56:78:9a:bc''') [Match] Name=dummy0 [Network] -Address=192.168.42.100 +Address=192.168.42.100/24 DNS=192.168.42.1 Domains={p}0 {p}1 {p}2 {p}3 {p}4'''.format(p=name_prefix)) - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-networkd') for timeout in range(50): with open(RESOLV_CONF) as f: @@ -937,18 +962,19 @@ MACAddress=12:34:56:78:9a:bc''') [Match] Name=dummy0 [Network] -Address=192.168.42.100 +Address=192.168.42.100/24 DNS=192.168.42.1''') self.write_network_dropin('test.network', 'dns', '''\ [Network] DNS=127.0.0.1''') - subprocess.check_call(['systemctl', 'start', 'systemd-networkd']) + self.start_unit('systemd-resolved') + self.start_unit('systemd-networkd') for timeout in range(50): with open(RESOLV_CONF) as f: contents = f.read() - if ' 127.0.0.1' in contents: + if ' 127.0.0.1' in contents and '192.168.42.1' in contents: break time.sleep(0.1) self.assertIn('nameserver 192.168.42.1\n', contents) diff --git a/test/test-execute/exec-environment-no-substitute.service b/test/test-execute/exec-environment-no-substitute.service new file mode 100644 index 000000000..6a2e60ec2 --- /dev/null +++ b/test/test-execute/exec-environment-no-substitute.service @@ -0,0 +1,8 @@ +[Unit] +Description=Test for No Environment Variable Substitution + +[Service] +ExecStart=/bin/sh -x -c 'test "$${VAR1-unset}" = "unset" && test "$${VAR2}" = "word3" && test "$${VAR3-unset}" = \'$word 5 6\'' +ExecStart=:/bin/sh -x -c 'test "$${VAR1-unset}" != "unset" && test "$${VAR2}" != "word3" && test "$${VAR3-unset}" != \'$word 5 6\'' +Type=oneshot +Environment="VAR2=word3" "VAR3=$word 5 6" diff --git a/test/test-execute/exec-inaccessiblepaths-proc.service b/test/test-execute/exec-inaccessiblepaths-sys.service similarity index 63% rename from test/test-execute/exec-inaccessiblepaths-proc.service rename to test/test-execute/exec-inaccessiblepaths-sys.service index ebdb4843d..5ff6f3605 100644 --- a/test/test-execute/exec-inaccessiblepaths-proc.service +++ b/test/test-execute/exec-inaccessiblepaths-sys.service @@ -2,6 +2,6 @@ Description=Test to make sure that mount namespace setup works properly with the 'InaccessiblePaths=/proc' option [Service] -InaccessiblePaths=/proc -ExecStart=/bin/sh -x -c 'test "$$(stat -c %%a /proc)" = "0"' +InaccessiblePaths=/sys +ExecStart=/bin/sh -x -c 'test "$$(stat -c %%a /sys)" = "0"' Type=oneshot diff --git a/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service b/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service new file mode 100644 index 000000000..1522ff80e --- /dev/null +++ b/test/test-execute/exec-protecthome-tmpfs-vs-protectsystem-strict.service @@ -0,0 +1,9 @@ +[Unit] +Description=Test ProtectHome=tmpfs vs ProtectSystem=strict +# Test for #11276 + +[Service] +ProtectHome=tmpfs +ProtectSystem=strict +Type=oneshot +ExecStart=/bin/sh -x -c 'test "$$(stat -fc %%T /home)" = "tmpfs"' diff --git a/test/test-functions b/test/test-functions index a936202e4..7179f70e4 100644 --- a/test/test-functions +++ b/test/test-functions @@ -24,8 +24,8 @@ fi 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" +BASICTOOLS="test sh bash setsid loadkeys setfont login sulogin gzip sleep echo head tail cat mount umount cryptsetup date dmsetup modprobe sed cmp tee rm true false chmod chown ln xargs" +DEBUGTOOLS="df free ls stty ps ln ip route dmesg dhclient mkdir cp ping dhclient strace less grep id tty touch du sort hostname find vi mv" STATEDIR="${BUILD_DIR:-.}/test/$(basename $(dirname $(realpath $0)))" STATEFILE="$STATEDIR/.testdir" @@ -60,7 +60,7 @@ 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 # nested KVM is flaky still. - if [ `systemd-detect-virt -v` != kvm ] ; then + if [[ $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then [ "$QEMU_BIN" ] || QEMU_BIN=$(which -a kvm qemu-kvm 2>/dev/null | grep '^/' -m1) fi @@ -134,7 +134,16 @@ run_qemu() { fi fi - [ "$QEMU_SMP" ] || QEMU_SMP=1 + # If QEMU_SMP was not explicitly set, try to determine the value 'dynamically' + # i.e. use the number of online CPUs on the host machine. If the nproc utility + # is not installed or there's some other error when calling it, fall back + # to the original value (QEMU_SMP=1). + if ! [ "$QEMU_SMP" ]; then + if ! QEMU_SMP=$(nproc); then + dwarn "nproc utility is not installed, falling back to QEMU_SMP=1" + QEMU_SMP=1 + fi + fi find_qemu_bin || return 1 @@ -152,6 +161,8 @@ run_qemu() { if [[ "$LOOKS_LIKE_SUSE" ]]; then PARAMS+="rd.hostonly=0" +elif [[ "$LOOKS_LIKE_ARCH" ]]; then + PARAMS+="rw" else PARAMS+="ro" fi @@ -181,7 +192,7 @@ $KERNEL_APPEND \ fi # Let's use KVM if it is available, but let's avoid using nested KVM as that is still flaky - if [ -c /dev/kvm -a `systemd-detect-virt -v` != kvm ]; then + if [[ -c /dev/kvm && $(systemd-detect-virt -v) != kvm && -z $TEST_NO_KVM ]] ; then QEMU_OPTIONS="$QEMU_OPTIONS -machine accel=kvm -enable-kvm -cpu host" fi @@ -459,7 +470,7 @@ create_empty_image() { [ -b "$LOOPDEV" ] || return 1 echo "LOOPDEV=$LOOPDEV" >> $STATEFILE sfdisk "$LOOPDEV" < undef, ); } - print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}' + print $2 if /\s(\S*)\[(\d+)\]:\s*SUMMARY:\s+\w+Sanitizer/ && !exists $services_to_ignore{$1}' ) if [[ ! -z "$pids" ]]; then ret=$(($ret+1)) @@ -644,6 +655,21 @@ install_basic_tools() { install_debug_tools() { [[ $DEBUGTOOLS ]] && dracut_install $DEBUGTOOLS + + if [[ $INTERACTIVE_DEBUG ]]; then + # Set default TERM from vt220 to linux, so at least basic key shortcuts work + local _getty_override="$initdir/etc/systemd/system/serial-getty@.service.d" + mkdir -p "$_getty_override" + echo -e "[Service]\nEnvironment=TERM=linux" > "$_getty_override/default-TERM.conf" + + cat > "$initdir/etc/motd" << EOF +To adjust the terminal size use: + export COLUMNS=xx + export LINES=yy +or + stty cols xx rows yy +EOF + fi } install_libnss() { @@ -663,6 +689,9 @@ install_dbus() { else inst $ROOTLIBDIR/system/dbus.service fi + # Newer Fedora versions use dbus-broker by default. Let's install it is available. + [ -f /usr/bin/dbus-broker ] && inst /usr/bin/dbus-broker + [ -f /usr/bin/dbus-broker-launch ] && inst /usr/bin/dbus-broker-launch find \ /etc/dbus-1 /usr/share/dbus-1 -xtype f \ @@ -747,7 +776,8 @@ setup_testsuite() { mkdir -p $initdir/etc/systemd/system/testsuite.target.wants ln -fs $TEST_BASE_DIR/testsuite.service $initdir/etc/systemd/system/testsuite.target.wants/testsuite.service - ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service + # Don't shutdown the machine after running the test when INTERACTIVE_DEBUG is set + [[ -z $INTERACTIVE_DEBUG ]] && ln -fs $TEST_BASE_DIR/end.service $initdir/etc/systemd/system/testsuite.target.wants/end.service # make the testsuite the default target ln -fs testsuite.target $initdir/etc/systemd/system/default.target @@ -808,8 +838,13 @@ inst_libs() { import_testdir() { [[ -e $STATEFILE ]] && . $STATEFILE - if [[ -z "$TESTDIR" ]] || [[ ! -d "$TESTDIR" ]]; then - TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX) + if [[ ! -d "$TESTDIR" ]]; then + if [[ -z "$TESTDIR" ]]; then + TESTDIR=$(mktemp --tmpdir=/var/tmp -d -t systemd-test.XXXXXX) + else + mkdir -p "$TESTDIR" + fi + echo "TESTDIR=\"$TESTDIR\"" > $STATEFILE export TESTDIR fi diff --git a/test/test-network/conf/11-dummy.network b/test/test-network/conf/11-dummy.network new file mode 100644 index 000000000..b117028a8 --- /dev/null +++ b/test/test-network/conf/11-dummy.network @@ -0,0 +1,5 @@ +[Match] +Name=test1 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/15-name-conflict-test.netdev b/test/test-network/conf/15-name-conflict-test.netdev new file mode 100644 index 000000000..c9d18d76b --- /dev/null +++ b/test/test-network/conf/15-name-conflict-test.netdev @@ -0,0 +1,4 @@ +[NetDev] +Name=dropin-test +Kind=dummy +MACAddress=00:50:56:c0:00:38 diff --git a/test/test-network/conf/21-macvlan.netdev b/test/test-network/conf/21-macvlan.netdev index e9a3c5b34..674c1f516 100644 --- a/test/test-network/conf/21-macvlan.netdev +++ b/test/test-network/conf/21-macvlan.netdev @@ -1,3 +1,4 @@ [NetDev] Name=macvlan99 Kind=macvlan +MTUBytes=2000 diff --git a/test/test-network/conf/21-vlan-test1.network b/test/test-network/conf/21-vlan-test1.network new file mode 100644 index 000000000..afe1debe0 --- /dev/null +++ b/test/test-network/conf/21-vlan-test1.network @@ -0,0 +1,2 @@ +[Match] +Name=test1 diff --git a/test/test-network/conf/21-vlan-test1.network.d/override.conf b/test/test-network/conf/21-vlan-test1.network.d/override.conf new file mode 100644 index 000000000..06307ff3d --- /dev/null +++ b/test/test-network/conf/21-vlan-test1.network.d/override.conf @@ -0,0 +1,5 @@ +[Network] +VLAN=vlan99 +Address=192.168.24.5/24 +Address=192.168.25.5/24 +IPv6AcceptRA=false diff --git a/test/test-network/conf/21-vlan.netdev.d/override.conf b/test/test-network/conf/21-vlan.netdev.d/override.conf index cd61be531..3b8d47d9b 100644 --- a/test/test-network/conf/21-vlan.netdev.d/override.conf +++ b/test/test-network/conf/21-vlan.netdev.d/override.conf @@ -1,3 +1,6 @@ +[NetDev] +MTUBytes=2000 + [VLAN] Id=99 GVRP=true diff --git a/test/test-network/conf/21-vlan.network b/test/test-network/conf/21-vlan.network index afe1debe0..0cd901def 100644 --- a/test/test-network/conf/21-vlan.network +++ b/test/test-network/conf/21-vlan.network @@ -1,2 +1,6 @@ [Match] -Name=test1 +Name=vlan99 + +[Network] +IPv6AcceptRA=false +Address=192.168.23.5/24 diff --git a/test/test-network/conf/21-vlan.network.d/override.conf b/test/test-network/conf/21-vlan.network.d/override.conf deleted file mode 100644 index 363fc90d7..000000000 --- a/test/test-network/conf/21-vlan.network.d/override.conf +++ /dev/null @@ -1,2 +0,0 @@ -[Network] -VLAN=vlan99 diff --git a/test/test-network/conf/25-address-preferred-lifetime-zero-ipv6.network b/test/test-network/conf/25-address-preferred-lifetime-zero-ipv6.network new file mode 100644 index 000000000..bfb278f31 --- /dev/null +++ b/test/test-network/conf/25-address-preferred-lifetime-zero-ipv6.network @@ -0,0 +1,27 @@ +[Match] +Name=dummy98 + +[Network] +# these lines are ignored +Address=hogehoge +Address=foofoo + +[Address] +Address=10.2.3.4/16 +PreferredLifetime=0 +Scope=link + +[Address] +Address=2001:0db8:0:f101::1/64 + +[Address] +# this section must be ignored +Peer=hoge +Address=10.10.0.1/16 +Label=30 + +[Address] +# this section must be ignored +Label=30 +Peer=hoge +Address=10.10.0.2/16 diff --git a/test/test-network/conf/25-address-section-miscellaneous.network b/test/test-network/conf/25-address-section-miscellaneous.network deleted file mode 100644 index 3a37d036c..000000000 --- a/test/test-network/conf/25-address-section-miscellaneous.network +++ /dev/null @@ -1,10 +0,0 @@ -[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 deleted file mode 100644 index 390495344..000000000 --- a/test/test-network/conf/25-address-section.network +++ /dev/null @@ -1,18 +0,0 @@ -[Match] -Name=dummy98 - -[Network] -IPv6AcceptRA=no - -[Address] -Address=10.2.3.4/16 -Peer=10.2.3.5/16 -Label=32 - -[Address] -Address=10.6.7.8/16 -Label=33 - -[Address] -Address=2001:db8::20 -Peer=2001:db8::10/128 diff --git a/test/test-network/conf/25-address-static.network b/test/test-network/conf/25-address-static.network new file mode 100644 index 000000000..e9780f2ee --- /dev/null +++ b/test/test-network/conf/25-address-static.network @@ -0,0 +1,53 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.1.2.3/16 +Address=10.1.2.4/16 +Address=10.2.2.4/16 +Address=2001:db8:0:f101::15/64 +Address=2001:db8:0:f101::16/64 +Address=2001:db8:0:f102::15/64 +# these lines are ignored +Address=hogehoge +Address=foofoo + +[Address] +Address=10.3.2.3/16 +Label=32 + +[Address] +Address=10.4.2.3/16 +Peer=10.4.2.4/16 +Label=33 + +[Address] +Address=0.0.0.0/24 +Label=34 + +[Address] +Address=0.0.0.0/16 +Label=35 + +[Address] +# this section must be ignored +Peer=hoge +Address=10.10.0.1/16 +Label=30 + +[Address] +# this section must be ignored +Label=30 +Peer=hoge +Address=10.10.0.2/16 + +[Address] +Address=2001:db8:0:f102::16/64 + +[Address] +Address=2001:db8:0:f103::20/128 +Peer=2001:db8:0:f103::10/128 + +[Address] +Address=::/64 diff --git a/test/test-network/conf/25-bind-carrier.network b/test/test-network/conf/25-bind-carrier.network new file mode 100644 index 000000000..cf854d351 --- /dev/null +++ b/test/test-network/conf/25-bind-carrier.network @@ -0,0 +1,8 @@ +[Match] +Name=test1 + +[Network] +BindCarrier=dummy99 dummy98 +Address=192.168.10.30/24 +Gateway=192.168.10.1 +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-bridge.network b/test/test-network/conf/25-bridge.network new file mode 100644 index 000000000..d2f346388 --- /dev/null +++ b/test/test-network/conf/25-bridge.network @@ -0,0 +1,5 @@ +[Match] +Name=bridge99 + +[Network] +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-erspan-tunnel-local-any.netdev b/test/test-network/conf/25-erspan-tunnel-local-any.netdev new file mode 100644 index 000000000..e9eb1c1cf --- /dev/null +++ b/test/test-network/conf/25-erspan-tunnel-local-any.netdev @@ -0,0 +1,10 @@ +[NetDev] +Name=erspan98 +Kind=erspan + +[Tunnel] +ERSPANIndex=124 +Local = any +Remote = 172.16.1.100 +Key=102 +SerializeTunneledPackets=true diff --git a/test/test-network/conf/25-erspan-tunnel.netdev b/test/test-network/conf/25-erspan-tunnel.netdev index 746b7ac64..86935e998 100644 --- a/test/test-network/conf/25-erspan-tunnel.netdev +++ b/test/test-network/conf/25-erspan-tunnel.netdev @@ -1,9 +1,8 @@ [NetDev] -Name=erspan-test +Name=erspan99 Kind=erspan [Tunnel] -Independent=true ERSPANIndex=123 Local = 172.16.1.200 Remote = 172.16.1.100 diff --git a/test/test-network/conf/25-fou-gre.netdev b/test/test-network/conf/25-fou-gre.netdev new file mode 100644 index 000000000..6ace606ed --- /dev/null +++ b/test/test-network/conf/25-fou-gre.netdev @@ -0,0 +1,14 @@ +[NetDev] +Name=gretun96 +Kind=gre + +[Tunnel] +Local=10.65.223.238 +Remote=10.65.223.239 +Key=1.2.5.103 +SerializeTunneledPackets=true +Independent=true + +FooOverUDP=yes +FOUDestinationPort=55556 +FOUSourcePort=1001 diff --git a/test/test-network/conf/25-fou-gretap.netdev b/test/test-network/conf/25-fou-gretap.netdev new file mode 100644 index 000000000..97fbc13d3 --- /dev/null +++ b/test/test-network/conf/25-fou-gretap.netdev @@ -0,0 +1,13 @@ +[NetDev] +Name=gretap96 +Kind=gretap + +[Tunnel] +Local=10.65.223.238 +Remote=10.65.223.239 +Key=1.2.5.106 +SerializeTunneledPackets=true +Independent=true + +FooOverUDP=yes +FOUDestinationPort=55556 diff --git a/test/test-network/conf/25-fou-ipip.netdev b/test/test-network/conf/25-fou-ipip.netdev new file mode 100644 index 000000000..2f5215749 --- /dev/null +++ b/test/test-network/conf/25-fou-ipip.netdev @@ -0,0 +1,12 @@ +[NetDev] +Name=ipiptun96 +Kind=ipip +MTUBytes=1480 + +[Tunnel] +Local=192.168.223.238 +Remote=192.169.224.239 +Independent=true + +FooOverUDP=yes +FOUDestinationPort=55555 diff --git a/test/test-network/conf/25-fou-ipproto-gre.netdev b/test/test-network/conf/25-fou-ipproto-gre.netdev new file mode 100644 index 000000000..09ecd9efb --- /dev/null +++ b/test/test-network/conf/25-fou-ipproto-gre.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=fou98 +Kind=fou + +[FooOverUDP] +Encapsulation=FooOverUDP +Port=55556 +Protocol=GRE diff --git a/test/test-network/conf/25-fou-ipproto-ipip.netdev b/test/test-network/conf/25-fou-ipproto-ipip.netdev new file mode 100644 index 000000000..b00d5ce58 --- /dev/null +++ b/test/test-network/conf/25-fou-ipproto-ipip.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=fou99 +Kind=fou + +[FooOverUDP] +Encapsulation=FooOverUDP +Port=55555 +Protocol=ipip diff --git a/test/test-network/conf/25-fou-sit.netdev b/test/test-network/conf/25-fou-sit.netdev new file mode 100644 index 000000000..f1b4f3257 --- /dev/null +++ b/test/test-network/conf/25-fou-sit.netdev @@ -0,0 +1,11 @@ +[NetDev] +Name=sittun96 +Kind=sit + +[Tunnel] +Local=10.65.223.238 +Remote=10.65.223.239 +Independent=true + +FooOverUDP=yes +FOUDestinationPort=55555 diff --git a/test/test-network/conf/25-gre-tunnel-local-any.netdev b/test/test-network/conf/25-gre-tunnel-local-any.netdev new file mode 100644 index 000000000..36bc55690 --- /dev/null +++ b/test/test-network/conf/25-gre-tunnel-local-any.netdev @@ -0,0 +1,9 @@ +[NetDev] +Name=gretun98 +Kind=gre + +[Tunnel] +Local=any +Remote=10.65.223.239 +Key=104 +SerializeTunneledPackets=false diff --git a/test/test-network/conf/25-gre-tunnel-remote-any.netdev b/test/test-network/conf/25-gre-tunnel-remote-any.netdev new file mode 100644 index 000000000..74c02c9c8 --- /dev/null +++ b/test/test-network/conf/25-gre-tunnel-remote-any.netdev @@ -0,0 +1,9 @@ +[NetDev] +Name=gretun97 +Kind=gre + +[Tunnel] +Local=10.65.223.238 +Remote=any +Key=105 +SerializeTunneledPackets=false diff --git a/test/test-network/conf/25-gre-tunnel.netdev b/test/test-network/conf/25-gre-tunnel.netdev index 94d9320cd..89c7fe126 100644 --- a/test/test-network/conf/25-gre-tunnel.netdev +++ b/test/test-network/conf/25-gre-tunnel.netdev @@ -5,3 +5,6 @@ Kind=gre [Tunnel] Local=10.65.223.238 Remote=10.65.223.239 +InputKey=1.2.3.103 +OutputKey=1.2.4.103 +SerializeTunneledPackets=true diff --git a/test/test-network/conf/25-gretap-tunnel-local-any.netdev b/test/test-network/conf/25-gretap-tunnel-local-any.netdev new file mode 100644 index 000000000..17abcf69f --- /dev/null +++ b/test/test-network/conf/25-gretap-tunnel-local-any.netdev @@ -0,0 +1,9 @@ +[NetDev] +Name=gretap98 +Kind=gretap + +[Tunnel] +Local=any +Remote=10.65.223.239 +Key=107 +SerializeTunneledPackets=true diff --git a/test/test-network/conf/25-gretap-tunnel.netdev b/test/test-network/conf/25-gretap-tunnel.netdev index 769e7653e..912daf5bf 100644 --- a/test/test-network/conf/25-gretap-tunnel.netdev +++ b/test/test-network/conf/25-gretap-tunnel.netdev @@ -5,3 +5,5 @@ Kind=gretap [Tunnel] Local=10.65.223.238 Remote=10.65.223.239 +Key=106 +SerializeTunneledPackets=true diff --git a/test/test-network/conf/25-ip6gre-tunnel-local-any.netdev b/test/test-network/conf/25-ip6gre-tunnel-local-any.netdev new file mode 100644 index 000000000..b3781f027 --- /dev/null +++ b/test/test-network/conf/25-ip6gre-tunnel-local-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=ip6gretun98 +Kind=ip6gre + +[Tunnel] +Local=any +Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/conf/25-ip6gre-tunnel-remote-any.netdev b/test/test-network/conf/25-ip6gre-tunnel-remote-any.netdev new file mode 100644 index 000000000..828c17f2a --- /dev/null +++ b/test/test-network/conf/25-ip6gre-tunnel-remote-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=ip6gretun97 +Kind=ip6gre + +[Tunnel] +Local=2a00:ffde:4567:edde::4987 +Remote=any diff --git a/test/test-network/conf/25-ip6gre-tunnel.netdev b/test/test-network/conf/25-ip6gre-tunnel.netdev index b16e0b496..ba7d2bc37 100644 --- a/test/test-network/conf/25-ip6gre-tunnel.netdev +++ b/test/test-network/conf/25-ip6gre-tunnel.netdev @@ -1,6 +1,6 @@ [NetDev] -Name=ip6gretap99 -Kind=ip6gretap +Name=ip6gretun99 +Kind=ip6gre [Tunnel] Local=2a00:ffde:4567:edde::4987 diff --git a/test/test-network/conf/25-ip6gretap-tunnel-local-any.netdev b/test/test-network/conf/25-ip6gretap-tunnel-local-any.netdev new file mode 100644 index 000000000..7a962e8a8 --- /dev/null +++ b/test/test-network/conf/25-ip6gretap-tunnel-local-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=ip6gretap98 +Kind=ip6gretap + +[Tunnel] +Local=any +Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/conf/25-ip6gretap-tunnel.netdev b/test/test-network/conf/25-ip6gretap-tunnel.netdev new file mode 100644 index 000000000..b16e0b496 --- /dev/null +++ b/test/test-network/conf/25-ip6gretap-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-local-any.netdev b/test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev new file mode 100644 index 000000000..7732eb881 --- /dev/null +++ b/test/test-network/conf/25-ip6tnl-tunnel-local-any.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=ip6tnl98 +Kind=ip6tnl + +[Tunnel] +Mode=ip6ip6 +Local=any +Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev b/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev new file mode 100644 index 000000000..0d9d1e900 --- /dev/null +++ b/test/test-network/conf/25-ip6tnl-tunnel-remote-any.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=ip6tnl97 +Kind=ip6tnl + +[Tunnel] +Mode=ip6ip6 +Local=2a00:ffde:4567:edde::4987 +Remote=any diff --git a/test/test-network/conf/25-ipip-tunnel-local-any.netdev b/test/test-network/conf/25-ipip-tunnel-local-any.netdev new file mode 100644 index 000000000..8fa27e82f --- /dev/null +++ b/test/test-network/conf/25-ipip-tunnel-local-any.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=ipiptun98 +Kind=ipip +MTUBytes=1480 + +[Tunnel] +Local=any +Remote=192.169.224.239 diff --git a/test/test-network/conf/25-ipip-tunnel-remote-any.netdev b/test/test-network/conf/25-ipip-tunnel-remote-any.netdev new file mode 100644 index 000000000..58d7feb61 --- /dev/null +++ b/test/test-network/conf/25-ipip-tunnel-remote-any.netdev @@ -0,0 +1,8 @@ +[NetDev] +Name=ipiptun97 +Kind=ipip +MTUBytes=1480 + +[Tunnel] +Local=192.168.223.238 +Remote=any diff --git a/test/test-network/conf/25-l2tp-dummy.network b/test/test-network/conf/25-l2tp-dummy.network new file mode 100644 index 000000000..9f2eb9095 --- /dev/null +++ b/test/test-network/conf/25-l2tp-dummy.network @@ -0,0 +1,7 @@ +[Match] +Name=test1 + +[Network] +Address=192.168.30.100/24 +IPv6AcceptRA=false +L2TP=l2tp99 diff --git a/test/test-network/conf/25-l2tp-ip.netdev b/test/test-network/conf/25-l2tp-ip.netdev new file mode 100644 index 000000000..ceae25fa5 --- /dev/null +++ b/test/test-network/conf/25-l2tp-ip.netdev @@ -0,0 +1,20 @@ +[NetDev] +Kind=l2tp +Name=l2tp99 + +[L2TP] +TunnelId=10 +PeerTunnelId=12 +Local=static +Remote=192.168.30.101 +EncapsulationType=ip + +[L2TPSession] +SessionId=25 +PeerSessionId=26 +Name=l2tp-ses3 + +[L2TPSession] +SessionId=27 +PeerSessionId=28 +Name=l2tp-ses4 diff --git a/test/test-network/conf/25-l2tp-udp.netdev b/test/test-network/conf/25-l2tp-udp.netdev new file mode 100644 index 000000000..84589b0b2 --- /dev/null +++ b/test/test-network/conf/25-l2tp-udp.netdev @@ -0,0 +1,25 @@ +[NetDev] +Kind=l2tp +Name=l2tp99 + +[L2TP] +TunnelId=10 +PeerTunnelId=11 +UDPSourcePort=3000 +UDPDestinationPort=4000 +Local=static +Remote=192.168.30.101 +EncapsulationType=udp +UDPCheckSum=true +UDP6CheckSumRx=true +UDP6CheckSumTx=true + +[L2TPSession] +SessionId=15 +PeerSessionId=16 +Name=l2tp-ses1 + +[L2TPSession] +SessionId=17 +PeerSessionId=18 +Name=l2tp-ses2 diff --git a/test/test-network/conf/25-link-local-addressing-yes.network b/test/test-network/conf/25-link-local-addressing-yes.network index dcf2f2f7b..8268da5aa 100644 --- a/test/test-network/conf/25-link-local-addressing-yes.network +++ b/test/test-network/conf/25-link-local-addressing-yes.network @@ -3,3 +3,4 @@ Name=test1 [Network] LinkLocalAddressing=yes +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-route-gateway-on-link.network b/test/test-network/conf/25-route-gateway-on-link.network deleted file mode 100644 index aa64c2551..000000000 --- a/test/test-network/conf/25-route-gateway-on-link.network +++ /dev/null @@ -1,13 +0,0 @@ -[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 deleted file mode 100644 index 29d07f182..000000000 --- a/test/test-network/conf/25-route-gateway.network +++ /dev/null @@ -1,14 +0,0 @@ -# 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 deleted file mode 100644 index d1b47ad43..000000000 --- a/test/test-network/conf/25-route-reverse-order.network +++ /dev/null @@ -1,15 +0,0 @@ -[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 deleted file mode 100644 index c9c7a722c..000000000 --- a/test/test-network/conf/25-route-section.network +++ /dev/null @@ -1,8 +0,0 @@ -[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-static.network b/test/test-network/conf/25-route-static.network new file mode 100644 index 000000000..50b2ce0c3 --- /dev/null +++ b/test/test-network/conf/25-route-static.network @@ -0,0 +1,46 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=2001:1234:5:8f63::1/128 +Address=149.10.124.58/28 + +[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 + +[Route] +Destination=149.10.124.64 +Scope=link + +[Route] +Gateway=149.10.124.64 + +[Route] +Gateway=149.10.125.65 +GatewayOnLink=yes + +[Route] +Destination=192.168.1.1 +InitialCongestionWindow=20 + +[Route] +Destination=192.168.1.2 +InitialAdvertisedReceiveWindow=30 + +[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-route-tcp-window-settings.network b/test/test-network/conf/25-route-tcp-window-settings.network deleted file mode 100644 index e77a72174..000000000 --- a/test/test-network/conf/25-route-tcp-window-settings.network +++ /dev/null @@ -1,10 +0,0 @@ -[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 deleted file mode 100644 index 9a104137d..000000000 --- a/test/test-network/conf/25-route-type.network +++ /dev/null @@ -1,14 +0,0 @@ -[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-local-any.netdev b/test/test-network/conf/25-sit-tunnel-local-any.netdev new file mode 100644 index 000000000..20c1a334a --- /dev/null +++ b/test/test-network/conf/25-sit-tunnel-local-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=sittun98 +Kind=sit + +[Tunnel] +Local=any +Remote=10.65.223.239 diff --git a/test/test-network/conf/25-sit-tunnel-remote-any.netdev b/test/test-network/conf/25-sit-tunnel-remote-any.netdev new file mode 100644 index 000000000..ed7b9b749 --- /dev/null +++ b/test/test-network/conf/25-sit-tunnel-remote-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=sittun97 +Kind=sit + +[Tunnel] +Local=10.65.223.238 +Remote=any diff --git a/test/test-network/conf/25-sysctl-disable-ipv6.network b/test/test-network/conf/25-sysctl-disable-ipv6.network new file mode 100644 index 000000000..e52078e0d --- /dev/null +++ b/test/test-network/conf/25-sysctl-disable-ipv6.network @@ -0,0 +1,6 @@ +[Match] +Name=dummy98 + +[Network] +IPv6AcceptRA=no +Address=10.2.3.4/16 diff --git a/test/test-network/conf/25-sysctl.network b/test/test-network/conf/25-sysctl.network index 2452fb7e8..68be30547 100644 --- a/test/test-network/conf/25-sysctl.network +++ b/test/test-network/conf/25-sysctl.network @@ -8,3 +8,4 @@ IPv6DuplicateAddressDetection=3 IPv6HopLimit=5 IPv4ProxyARP=true IPv6ProxyNDP=true +IPv6AcceptRA=no diff --git a/test/test-network/conf/25-vrf.network b/test/test-network/conf/25-vrf.network new file mode 100644 index 000000000..42ce5b192 --- /dev/null +++ b/test/test-network/conf/25-vrf.network @@ -0,0 +1,2 @@ +[Match] +Name=vrf99 diff --git a/test/test-network/conf/25-vti-tunnel-local-any.netdev b/test/test-network/conf/25-vti-tunnel-local-any.netdev new file mode 100644 index 000000000..cab388643 --- /dev/null +++ b/test/test-network/conf/25-vti-tunnel-local-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=vtitun98 +Kind=vti + +[Tunnel] +Local=remote +Remote=10.65.223.239 diff --git a/test/test-network/conf/25-vti-tunnel-remote-any.netdev b/test/test-network/conf/25-vti-tunnel-remote-any.netdev new file mode 100644 index 000000000..b8bedffaa --- /dev/null +++ b/test/test-network/conf/25-vti-tunnel-remote-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=vtitun97 +Kind=vti + +[Tunnel] +Local=10.65.223.238 +Remote=any diff --git a/test/test-network/conf/25-vti6-tunnel-local-any.netdev b/test/test-network/conf/25-vti6-tunnel-local-any.netdev new file mode 100644 index 000000000..c3d05b4f5 --- /dev/null +++ b/test/test-network/conf/25-vti6-tunnel-local-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=vti6tun98 +Kind=vti6 + +[Tunnel] +Local=any +Remote=2001:473:fece:cafe::5179 diff --git a/test/test-network/conf/25-vti6-tunnel-remote-any.netdev b/test/test-network/conf/25-vti6-tunnel-remote-any.netdev new file mode 100644 index 000000000..b86c628ab --- /dev/null +++ b/test/test-network/conf/25-vti6-tunnel-remote-any.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=vti6tun97 +Kind=vti6 + +[Tunnel] +Local=2a00:ffde:4567:edde::4987 +Remote=any diff --git a/test/test-network/conf/25-wireguard-23-peers.netdev b/test/test-network/conf/25-wireguard-23-peers.netdev index 7f77dc174..61a75e592 100644 --- a/test/test-network/conf/25-wireguard-23-peers.netdev +++ b/test/test-network/conf/25-wireguard-23-peers.netdev @@ -7,7 +7,9 @@ Description=For issue #11404 [WireGuard] # 51820 is common port for Wireguard, 4500 is IPSec/UDP ListenPort=4500 -PrivateKey=CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU= +# The key below should be overridden by PrivateKeyFile= +PrivateKey=EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong= +PrivateKeyFile=/run/systemd/network/25-wireguard-private-key.txt # peer 1 [WireGuardPeer] diff --git a/test/test-network/conf/25-wireguard-preshared-key.txt b/test/test-network/conf/25-wireguard-preshared-key.txt new file mode 100644 index 000000000..021c443ab --- /dev/null +++ b/test/test-network/conf/25-wireguard-preshared-key.txt @@ -0,0 +1,3 @@ +cPLOy1YUrEI0EM + MIycPJmOo0aTu3RZnw8bL5 + meVD6m0= diff --git a/test/test-network/conf/25-wireguard-private-key.txt b/test/test-network/conf/25-wireguard-private-key.txt new file mode 100644 index 000000000..469acd095 --- /dev/null +++ b/test/test-network/conf/25-wireguard-private-key.txt @@ -0,0 +1,6 @@ +CJQUtcS9emY2fLY + qDlpSZiE/QJyHkP + Wr+WHtZ + + +LZ90FU= diff --git a/test/test-network/conf/25-wireguard.netdev.d/peer.conf b/test/test-network/conf/25-wireguard.netdev.d/peer.conf new file mode 100644 index 000000000..f559ea68f --- /dev/null +++ b/test/test-network/conf/25-wireguard.netdev.d/peer.conf @@ -0,0 +1,5 @@ +[WireGuardPeer] +PublicKey=lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= +AllowedIPs=fdbc:bae2:7871:0500:e1fe:0793:8636:dad1/128 +AllowedIPs=fdbc:bae2:7871:e1fe:0793:8636::/96 +PresharedKeyFile=/run/systemd/network/25-wireguard-preshared-key.txt diff --git a/test/test-network/conf/25-wireguard.network b/test/test-network/conf/25-wireguard.network new file mode 100644 index 000000000..ab3052391 --- /dev/null +++ b/test/test-network/conf/25-wireguard.network @@ -0,0 +1,2 @@ +[Match] +Name=wg99 diff --git a/test/test-network/conf/26-bridge-slave-interface-1.network b/test/test-network/conf/26-bridge-slave-interface-1.network index 81b372fb6..1f8c5b564 100644 --- a/test/test-network/conf/26-bridge-slave-interface-1.network +++ b/test/test-network/conf/26-bridge-slave-interface-1.network @@ -9,4 +9,7 @@ Cost=400 HairPin = true FastLeave = true UnicastFlood = true +MulticastFlood = false MulticastToUnicast = true +NeighborSuppression = true +Learning = false diff --git a/test/test-network/conf/bond-slave.network b/test/test-network/conf/bond-slave.network new file mode 100644 index 000000000..4eeeae201 --- /dev/null +++ b/test/test-network/conf/bond-slave.network @@ -0,0 +1,12 @@ +[Match] +Name=dummy98 test1 + +[Network] +Bond=bond99 + +# Settings below should be ignored +IPv6AcceptRA=yes +LinkLocalAddressing=yes +DHCP=yes +Address=192.168.25.3/24 +Gateway=192.168.25.1 diff --git a/test/test-network/conf/bond99.network b/test/test-network/conf/bond99.network new file mode 100644 index 000000000..c5b417abb --- /dev/null +++ b/test/test-network/conf/bond99.network @@ -0,0 +1,6 @@ +[Match] +Name=bond99 + +[Network] +IPv6AcceptRA=no +Address=192.168.123.45/24 diff --git a/test/test-network/conf/bridge99-ignore-carrier-loss.network b/test/test-network/conf/bridge99-ignore-carrier-loss.network new file mode 100644 index 000000000..d4741d687 --- /dev/null +++ b/test/test-network/conf/bridge99-ignore-carrier-loss.network @@ -0,0 +1,12 @@ +[Match] +Name=bridge99 + +[Network] +Address=192.168.0.15/24 +Gateway=192.168.0.1 +IPv6AcceptRA=no +IgnoreCarrierLoss=true + +[RoutingPolicyRule] +To=8.8.8.8 +Table=100 diff --git a/test/test-network/conf/bridge99.network b/test/test-network/conf/bridge99.network index 39e48ce2e..1a00ec10d 100644 --- a/test/test-network/conf/bridge99.network +++ b/test/test-network/conf/bridge99.network @@ -4,3 +4,4 @@ Name=bridge99 [Network] Address=192.168.0.15/24 Gateway=192.168.0.1 +IPv6AcceptRA=no diff --git a/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network b/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network new file mode 100644 index 000000000..35aa8e7f5 --- /dev/null +++ b/test/test-network/conf/dhcp-client-gateway-onlink-implicit.network @@ -0,0 +1,13 @@ +[Match] +Name=veth99 + +[Network] +DHCP=ipv4 + +[Route] +Gateway=192.168.0.1 +Destination=10.0.0.0/8 + +[Route] +Gateway=192.168.0.1 +Destination=192.168.100.0/24 diff --git a/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network b/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network index 5c4ca22ab..ebe3b7c0d 100644 --- a/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network +++ b/test/test-network/conf/dhcp-client-ipv4-dhcp-settings.network @@ -13,3 +13,4 @@ UseHostname=true Hostname=test-hostname ClientIdentifier=mac VendorClassIdentifier=SusantVendorTest +RouteTable=211 diff --git a/test/test-network/conf/dhcp-client-vrf.network b/test/test-network/conf/dhcp-client-vrf.network new file mode 100644 index 000000000..bb1d2e09c --- /dev/null +++ b/test/test-network/conf/dhcp-client-vrf.network @@ -0,0 +1,8 @@ +[Match] +Name=veth99 + +[Network] +DHCP=yes +IPv6AcceptRA=yes +LinkLocalAddressing=yes +VRF=vrf99 diff --git a/test/test-network/conf/dhcp-server.network b/test/test-network/conf/dhcp-server.network index 9e49691a9..439258a53 100644 --- a/test/test-network/conf/dhcp-server.network +++ b/test/test-network/conf/dhcp-server.network @@ -3,6 +3,7 @@ Name=veth-peer [Network] Address=192.168.5.1/24 +IPv6AcceptRA=false DHCPServer=yes [DHCPServer] diff --git a/test/test-network/conf/erspan.network b/test/test-network/conf/erspan.network new file mode 100644 index 000000000..49364c506 --- /dev/null +++ b/test/test-network/conf/erspan.network @@ -0,0 +1,6 @@ +[Match] +Name=dummy98 + +[Network] +Tunnel=erspan99 +Tunnel=erspan98 diff --git a/test/test-network/conf/gretap.network b/test/test-network/conf/gretap.network index 88b925034..1493fcf2b 100644 --- a/test/test-network/conf/gretap.network +++ b/test/test-network/conf/gretap.network @@ -3,3 +3,4 @@ Name=dummy98 [Network] Tunnel=gretap99 +Tunnel=gretap98 diff --git a/test/test-network/conf/gretun.network b/test/test-network/conf/gretun.network index 376074cb1..00bb03cab 100644 --- a/test/test-network/conf/gretun.network +++ b/test/test-network/conf/gretun.network @@ -3,3 +3,5 @@ Name=dummy98 [Network] Tunnel=gretun99 +Tunnel=gretun98 +Tunnel=gretun97 diff --git a/test/test-network/conf/ip6gretap.network b/test/test-network/conf/ip6gretap.network index cad0bae04..7ae4e3aea 100644 --- a/test/test-network/conf/ip6gretap.network +++ b/test/test-network/conf/ip6gretap.network @@ -3,3 +3,4 @@ Name=dummy98 [Network] Tunnel=ip6gretap99 +Tunnel=ip6gretap98 diff --git a/test/test-network/conf/ip6gretun.network b/test/test-network/conf/ip6gretun.network new file mode 100644 index 000000000..6d39bbd77 --- /dev/null +++ b/test/test-network/conf/ip6gretun.network @@ -0,0 +1,7 @@ +[Match] +Name=dummy98 + +[Network] +Tunnel=ip6gretun99 +Tunnel=ip6gretun98 +Tunnel=ip6gretun97 diff --git a/test/test-network/conf/ip6tnl.network b/test/test-network/conf/ip6tnl.network index 41e344849..15c6d15d4 100644 --- a/test/test-network/conf/ip6tnl.network +++ b/test/test-network/conf/ip6tnl.network @@ -3,3 +3,5 @@ Name=dummy98 [Network] Tunnel=ip6tnl99 +Tunnel=ip6tnl98 +Tunnel=ip6tnl97 diff --git a/test/test-network/conf/ipip.network b/test/test-network/conf/ipip.network index 4ce671490..ec6c95811 100644 --- a/test/test-network/conf/ipip.network +++ b/test/test-network/conf/ipip.network @@ -3,3 +3,5 @@ Name=dummy98 [Network] Tunnel=ipiptun99 +Tunnel=ipiptun98 +Tunnel=ipiptun97 diff --git a/test/test-network/conf/routing-policy-rule-dummy98.network b/test/test-network/conf/routing-policy-rule-dummy98.network new file mode 100644 index 000000000..8136c20ae --- /dev/null +++ b/test/test-network/conf/routing-policy-rule-dummy98.network @@ -0,0 +1,10 @@ +[Match] +Name=dummy98 + +[RoutingPolicyRule] +TypeOfService=0x08 +Table=8 +From= 192.168.101.18 +Priority=112 +IncomingInterface=dummy98 +OutgoingInterface=dummy98 diff --git a/test/test-network/conf/routing-policy-rule.network b/test/test-network/conf/routing-policy-rule-test1.network similarity index 100% rename from test/test-network/conf/routing-policy-rule.network rename to test/test-network/conf/routing-policy-rule-test1.network diff --git a/test/test-network/conf/sit.network b/test/test-network/conf/sit.network index 84e5af0ff..8d97823a5 100644 --- a/test/test-network/conf/sit.network +++ b/test/test-network/conf/sit.network @@ -3,3 +3,5 @@ Name=dummy98 [Network] Tunnel=sittun99 +Tunnel=sittun98 +Tunnel=sittun97 diff --git a/test/test-network/conf/test-static.network b/test/test-network/conf/test-static.network deleted file mode 100644 index 636c55c8e..000000000 --- a/test/test-network/conf/test-static.network +++ /dev/null @@ -1,6 +0,0 @@ -[Match] -Name=dummy98 - -[Network] -Address=192.168.0.15/24 -Gateway=192.168.0.1 diff --git a/test/test-network/conf/vlan6.netdev b/test/test-network/conf/vlan6.netdev new file mode 100644 index 000000000..310be91fa --- /dev/null +++ b/test/test-network/conf/vlan6.netdev @@ -0,0 +1,7 @@ +[NetDev] +Name=vlan6 +Kind=vlan +MTUBytes=1500 + +[VLAN] +Id=6 diff --git a/test/test-network/conf/vlan6.network b/test/test-network/conf/vlan6.network new file mode 100644 index 000000000..64e9db520 --- /dev/null +++ b/test/test-network/conf/vlan6.network @@ -0,0 +1,6 @@ +[Match] +Name=vlan6 + +[Network] +IPv6AcceptRA=false +Address=100.100.100.2/24 diff --git a/test/test-network/conf/vti.network b/test/test-network/conf/vti.network index 7fbad6a82..1e0b8405c 100644 --- a/test/test-network/conf/vti.network +++ b/test/test-network/conf/vti.network @@ -3,3 +3,5 @@ Name=dummy98 [Network] Tunnel=vtitun99 +Tunnel=vtitun98 +Tunnel=vtitun97 diff --git a/test/test-network/conf/vti6.network b/test/test-network/conf/vti6.network index 49a9d11ff..60ccb77f5 100644 --- a/test/test-network/conf/vti6.network +++ b/test/test-network/conf/vti6.network @@ -3,3 +3,5 @@ Name=dummy98 [Network] Tunnel=vti6tun99 +Tunnel=vti6tun98 +Tunnel=vti6tun97 diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 1fc7094d2..b44cfe8d4 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -22,6 +22,8 @@ network_sysctl_ipv4_path='/proc/sys/net/ipv4/conf' dnsmasq_pid_file='/run/networkd-ci/test-test-dnsmasq.pid' dnsmasq_log_file='/run/networkd-ci/test-dnsmasq-log-file' +wait_online_bin='/usr/lib/systemd/systemd-networkd-wait-online' + 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) @@ -69,7 +71,6 @@ def expectedFailureIfRoutingPolicyIPProtoIsNotAvailable(): return f def setUpModule(): - os.makedirs(network_unit_file_path, exist_ok=True) os.makedirs(networkd_ci_path, exist_ok=True) @@ -86,8 +87,6 @@ def tearDownModule(): 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() @@ -110,6 +109,14 @@ class Utilities(): subprocess.call(['ip', 'link', 'del', 'dev', link]) time.sleep(1) + def l2tp_tunnel_remove(self, tunnel_ids): + output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel']).rstrip().decode('utf-8') + for tid in tunnel_ids: + words='Tunnel ' + tid + ', encap' + if words in output: + subprocess.call(['ip', 'l2tp', 'del', 'tunnel', 'tid', tid]) + 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() @@ -119,6 +126,7 @@ class Utilities(): return f.readline().strip() def copy_unit_to_networkd_unit_path(self, *units): + print() 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'))): @@ -166,15 +174,22 @@ class Utilities(): 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'))): + def start_networkd(self, sleep_sec=5, remove_state_files=True): + if (remove_state_files and + 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() + if sleep_sec > 0: + time.sleep(sleep_sec) + + def wait_online(self, links_with_operstate, timeout='20s', bool_any=False): + args = [wait_online_bin, f'--timeout={timeout}'] + [f'--interface={link}' for link in links_with_operstate] + if bool_any: + args += ['--any'] + subprocess.check_call(args) class NetworkdNetDevTests(unittest.TestCase, Utilities): @@ -184,17 +199,35 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'bridge99', 'dropin-test', 'dummy98', - 'erspan-test', + 'erspan98', + 'erspan99', 'geneve99', + 'gretap96', + 'gretap98', 'gretap99', + 'gretun96', + 'gretun97', + 'gretun98', 'gretun99', + 'ip6gretap98', 'ip6gretap99', + 'ip6gretun97', + 'ip6gretun98', + 'ip6gretun99', + 'ip6tnl97', + 'ip6tnl98', 'ip6tnl99', + 'ipiptun96', + 'ipiptun97', + 'ipiptun98', 'ipiptun99', 'ipvlan99', 'isataptun99', 'macvlan99', 'macvtap99', + 'sittun96', + 'sittun97', + 'sittun98', 'sittun99', 'tap99', 'test1', @@ -203,7 +236,11 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 'veth99', 'vlan99', 'vrf99', + 'vti6tun97', + 'vti6tun98', 'vti6tun99', + 'vtitun97', + 'vtitun98', 'vtitun99', 'vxlan99', 'wg98', @@ -212,42 +249,75 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): units = [ '10-dropin-test.netdev', '11-dummy.netdev', + '11-dummy.network', '12-dummy.netdev', + '15-name-conflict-test.netdev', '21-macvlan.netdev', '21-macvtap.netdev', + '21-vlan-test1.network', '21-vlan.netdev', '21-vlan.network', '25-6rd-tunnel.netdev', '25-bond.netdev', '25-bond-balanced-tlb.netdev', '25-bridge.netdev', + '25-bridge.network', + '25-erspan-tunnel-local-any.netdev', '25-erspan-tunnel.netdev', + '25-fou-gretap.netdev', + '25-fou-gre.netdev', + '25-fou-ipip.netdev', + '25-fou-ipproto-gre.netdev', + '25-fou-ipproto-ipip.netdev', + '25-fou-sit.netdev', '25-geneve.netdev', + '25-gretap-tunnel-local-any.netdev', '25-gretap-tunnel.netdev', + '25-gre-tunnel-local-any.netdev', + '25-gre-tunnel-remote-any.netdev', '25-gre-tunnel.netdev', + '25-ip6gretap-tunnel-local-any.netdev', + '25-ip6gretap-tunnel.netdev', + '25-ip6gre-tunnel-local-any.netdev', + '25-ip6gre-tunnel-remote-any.netdev', '25-ip6gre-tunnel.netdev', + '25-ip6tnl-tunnel-remote-any.netdev', + '25-ip6tnl-tunnel-local-any.netdev', '25-ip6tnl-tunnel.netdev', '25-ipip-tunnel-independent.netdev', + '25-ipip-tunnel-local-any.netdev', + '25-ipip-tunnel-remote-any.netdev', '25-ipip-tunnel.netdev', '25-ipvlan.netdev', '25-isatap-tunnel.netdev', + '25-sit-tunnel-local-any.netdev', + '25-sit-tunnel-remote-any.netdev', '25-sit-tunnel.netdev', '25-tap.netdev', '25-tun.netdev', '25-vcan.netdev', '25-veth.netdev', '25-vrf.netdev', + '25-vti6-tunnel-local-any.netdev', + '25-vti6-tunnel-remote-any.netdev', '25-vti6-tunnel.netdev', + '25-vti-tunnel-local-any.netdev', + '25-vti-tunnel-remote-any.netdev', '25-vti-tunnel.netdev', '25-vxlan.netdev', '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', + '25-wireguard-preshared-key.txt', + '25-wireguard-private-key.txt', '25-wireguard.netdev', + '25-wireguard.network', '6rd.network', + 'erspan.network', 'gre.network', 'gretap.network', 'gretun.network', 'ip6gretap.network', + 'ip6gretun.network', 'ip6tnl.network', 'ipip.network', 'ipvlan.network', @@ -267,15 +337,56 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.remove_unit_from_networkd_path(self.units) def test_dropin(self): - self.copy_unit_to_networkd_unit_path('10-dropin-test.netdev') + self.copy_unit_to_networkd_unit_path('10-dropin-test.netdev', '15-name-conflict-test.netdev') self.start_networkd() self.assertTrue(self.link_exits('dropin-test')) + # This also tests NetDev.Name= conflict and basic networkctl functionalities + output = subprocess.check_output(['ip', 'link', 'show', 'dropin-test']).rstrip().decode('utf-8') print(output) self.assertRegex(output, '00:50:56:c0:00:28') + output = subprocess.check_output(['networkctl', 'list']).rstrip().decode('utf-8') + self.assertRegex(output, '1 lo ') + self.assertRegex(output, 'dropin-test') + + output = subprocess.check_output(['networkctl', 'list', 'dropin-test']).rstrip().decode('utf-8') + self.assertNotRegex(output, '1 lo ') + self.assertRegex(output, 'dropin-test') + + output = subprocess.check_output(['networkctl', 'list', 'dropin-*']).rstrip().decode('utf-8') + self.assertNotRegex(output, '1 lo ') + self.assertRegex(output, 'dropin-test') + + output = subprocess.check_output(['networkctl', 'status', 'dropin-*']).rstrip().decode('utf-8') + self.assertNotRegex(output, '1: lo ') + self.assertRegex(output, 'dropin-test') + + ret = subprocess.run(['ethtool', '--driver', 'dropin-test'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + print(ret.stdout.rstrip().decode('utf-8')) + if ret.returncode == 0 and re.search('driver: dummy', ret.stdout.rstrip().decode('utf-8')) != None: + self.assertRegex(output, 'Driver: dummy') + else: + print('ethtool does not support driver field at least for dummy interfaces, skipping test for Driver field of networkctl.') + + def test_wait_online_any(self): + self.copy_unit_to_networkd_unit_path('25-bridge.netdev', '25-bridge.network', '11-dummy.netdev', '11-dummy.network') + self.start_networkd(0) + + self.wait_online(['bridge99', 'test1:degraded'], bool_any=True) + self.assertTrue(self.link_exits('bridge99')) + self.assertTrue(self.link_exits('test1')) + + output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: (?:off|no-carrier) \(configuring\)') + + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: degraded \(configured\)') + def test_bridge(self): self.copy_unit_to_networkd_unit_path('25-bridge.netdev') self.start_networkd() @@ -319,18 +430,34 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 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.copy_unit_to_networkd_unit_path('21-vlan.netdev', '11-dummy.netdev', + '21-vlan.network', '21-vlan-test1.network') self.start_networkd() + self.assertTrue(self.link_exits('test1')) self.assertTrue(self.link_exits('vlan99')) + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertTrue(output, ' mtu 2004 ') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vlan99']).rstrip().decode('utf-8') print(output) + self.assertTrue(output, ' mtu 2000 ') self.assertTrue(output, 'REORDER_HDR') self.assertTrue(output, 'LOOSE_BINDING') self.assertTrue(output, 'GVRP') self.assertTrue(output, 'MVRP') - self.assertTrue(output, '99') + self.assertTrue(output, ' id 99 ') + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 192.168.24.5/24 brd 192.168.24.255 scope global test1') + self.assertRegex(output, 'inet 192.168.25.5/24 brd 192.168.25.255 scope global test1') + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'vlan99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 192.168.23.5/24 brd 192.168.23.255 scope global vlan99') def test_macvtap(self): self.copy_unit_to_networkd_unit_path('21-macvtap.netdev', '11-dummy.netdev', 'macvtap.network') @@ -342,8 +469,17 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.copy_unit_to_networkd_unit_path('21-macvlan.netdev', '11-dummy.netdev', 'macvlan.network') self.start_networkd() + self.assertTrue(self.link_exits('test1')) self.assertTrue(self.link_exits('macvlan99')) + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertTrue(output, ' mtu 2000 ') + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'macvlan99']).rstrip().decode('utf-8') + print(output) + self.assertTrue(output, ' mtu 2000 ') + @expectedFailureIfModuleIsNotAvailable('ipvlan') def test_ipvlan(self): self.copy_unit_to_networkd_unit_path('25-ipvlan.netdev', '11-dummy.netdev', 'ipvlan.network') @@ -391,33 +527,37 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): @expectedFailureIfModuleIsNotAvailable('wireguard') def test_wireguard(self): - self.copy_unit_to_networkd_unit_path('25-wireguard.netdev') - self.start_networkd() + self.copy_unit_to_networkd_unit_path('25-wireguard.netdev', '25-wireguard.network', + '25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network', + '25-wireguard-preshared-key.txt', '25-wireguard-private-key.txt') + self.start_networkd(0) + self.wait_online(['wg99:carrier', 'wg98:routable']) + + self.assertTrue(self.link_exits('wg99')) + self.assertTrue(self.link_exits('wg98')) if shutil.which('wg'): subprocess.call('wg') + output = subprocess.check_output(['wg', 'show', 'wg99', 'listen-port']).rstrip().decode('utf-8') self.assertTrue(output, '51820') output = subprocess.check_output(['wg', 'show', 'wg99', 'fwmark']).rstrip().decode('utf-8') self.assertTrue(output, '0x4d2') output = subprocess.check_output(['wg', 'show', 'wg99', 'allowed-ips']).rstrip().decode('utf-8') self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.26.0/24 fd31:bf08:57cb::/48') + self.assertTrue(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc=\tfdbc:bae2:7871:e1fe:793:8636::/96 fdbc:bae2:7871:500:e1fe:793:8636:dad1/128') output = subprocess.check_output(['wg', 'show', 'wg99', 'persistent-keepalive']).rstrip().decode('utf-8') self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t20') output = subprocess.check_output(['wg', 'show', 'wg99', 'endpoints']).rstrip().decode('utf-8') self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA=\t192.168.27.3:51820') + output = subprocess.check_output(['wg', 'show', 'wg99', 'private-key']).rstrip().decode('utf-8') + self.assertTrue(output, 'EEGlnEPYJV//kbvvIqxKkQwOiS+UENyPncC4bF46ong=') + output = subprocess.check_output(['wg', 'show', 'wg99', 'preshared-keys']).rstrip().decode('utf-8') + self.assertTrue(output, 'RDf+LSpeEre7YEIKaxg+wbpsNV7du+ktR99uBEtIiCA= IIWIV17wutHv7t4cR6pOT91z6NSz/T8Arh0yaywhw3M=') + self.assertTrue(output, 'lsDtM3AbjxNlauRKzHEPfgS1Zp7cp/VX5Use/P4PQSc= cPLOy1YUrEI0EMMIycPJmOo0aTu3RZnw8bL5meVD6m0=') - self.assertTrue(self.link_exits('wg99')) - - @expectedFailureIfModuleIsNotAvailable('wireguard') - def test_wireguard_23_peers(self): - self.copy_unit_to_networkd_unit_path('25-wireguard-23-peers.netdev', '25-wireguard-23-peers.network') - self.start_networkd() - - if shutil.which('wg'): - subprocess.call('wg') - - self.assertTrue(self.link_exits('wg98')) + output = subprocess.check_output(['wg', 'show', 'wg98', 'private-key']).rstrip().decode('utf-8') + self.assertTrue(output, 'CJQUtcS9emY2fLYqDlpSZiE/QJyHkPWr+WHtZLZ90FU=') def test_geneve(self): self.copy_unit_to_networkd_unit_path('25-geneve.netdev') @@ -433,60 +573,197 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): 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.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ipip-tunnel.netdev', 'ipip.network', + '25-ipip-tunnel-local-any.netdev', '25-ipip-tunnel-remote-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('ipiptun99')) + self.assertTrue(self.link_exits('ipiptun98')) + self.assertTrue(self.link_exits('ipiptun97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ipip (?:ipip |)remote 192.169.224.239 local 192.168.223.238 dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ipip (?:ipip |)remote 192.169.224.239 local any dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ipip (?:ipip |)remote any local 192.168.223.238 dev dummy98') def test_gre_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gre-tunnel.netdev', 'gretun.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gre-tunnel.netdev', 'gretun.network', + '25-gre-tunnel-local-any.netdev', '25-gre-tunnel-remote-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('gretun99')) + self.assertTrue(self.link_exits('gretun98')) + self.assertTrue(self.link_exits('gretun97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'gre remote 10.65.223.239 local 10.65.223.238 dev dummy98') + self.assertRegex(output, 'ikey 1.2.3.103') + self.assertRegex(output, 'okey 1.2.4.103') + self.assertRegex(output, 'iseq') + self.assertRegex(output, 'oseq') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'gre remote 10.65.223.239 local any dev dummy98') + self.assertRegex(output, 'ikey 0.0.0.104') + self.assertRegex(output, 'okey 0.0.0.104') + self.assertNotRegex(output, 'iseq') + self.assertNotRegex(output, 'oseq') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'gre remote any local 10.65.223.238 dev dummy98') + self.assertRegex(output, 'ikey 0.0.0.105') + self.assertRegex(output, 'okey 0.0.0.105') + self.assertNotRegex(output, 'iseq') + self.assertNotRegex(output, 'oseq') + + def test_ip6gre_tunnel(self): + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gre-tunnel.netdev', 'ip6gretun.network', + '25-ip6gre-tunnel-local-any.netdev', '25-ip6gre-tunnel-remote-any.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('dummy98')) + self.assertTrue(self.link_exits('ip6gretun99')) + self.assertTrue(self.link_exits('ip6gretun98')) + self.assertTrue(self.link_exits('ip6gretun97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6gre remote 2001:473:fece:cafe::5179 local any dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretun97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6gre remote any local 2a00:ffde:4567:edde::4987 dev dummy98') def test_gretap_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gretap-tunnel.netdev', 'gretap.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-gretap-tunnel.netdev', 'gretap.network', + '25-gretap-tunnel-local-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('gretap99')) + self.assertTrue(self.link_exits('gretap98')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'gretap remote 10.65.223.239 local 10.65.223.238 dev dummy98') + self.assertRegex(output, 'ikey 0.0.0.106') + self.assertRegex(output, 'okey 0.0.0.106') + self.assertRegex(output, 'iseq') + self.assertRegex(output, 'oseq') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'gretap remote 10.65.223.239 local any dev dummy98') + self.assertRegex(output, 'ikey 0.0.0.107') + self.assertRegex(output, 'okey 0.0.0.107') + self.assertRegex(output, 'iseq') + self.assertRegex(output, 'oseq') def test_ip6gretap_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gre-tunnel.netdev', 'ip6gretap.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6gretap-tunnel.netdev', 'ip6gretap.network', + '25-ip6gretap-tunnel-local-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('ip6gretap99')) + self.assertTrue(self.link_exits('ip6gretap98')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6gretap98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6gretap remote 2001:473:fece:cafe::5179 local any dev dummy98') def test_vti_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti-tunnel.netdev', 'vti.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti-tunnel.netdev', 'vti.network', + '25-vti-tunnel-local-any.netdev', '25-vti-tunnel-remote-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('vtitun99')) + self.assertTrue(self.link_exits('vtitun98')) + self.assertTrue(self.link_exits('vtitun97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vti remote 10.65.223.239 local 10.65.223.238 dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vti remote 10.65.223.239 local any dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vtitun97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vti remote any local 10.65.223.238 dev dummy98') def test_vti6_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti6-tunnel.netdev', 'vti6.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-vti6-tunnel.netdev', 'vti6.network', + '25-vti6-tunnel-local-any.netdev', '25-vti6-tunnel-remote-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('vti6tun99')) + self.assertTrue(self.link_exits('vti6tun98')) + self.assertTrue(self.link_exits('vti6tun97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vti6 remote 2001:473:fece:cafe::5179 local (?:any|::) dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'vti6tun97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vti6 remote (?:any|::) local 2a00:ffde:4567:edde::4987 dev dummy98') def test_ip6tnl_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6tnl-tunnel.netdev', 'ip6tnl.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-ip6tnl-tunnel.netdev', 'ip6tnl.network', + '25-ip6tnl-tunnel-local-any.netdev', '25-ip6tnl-tunnel-remote-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('ip6tnl99')) + self.assertTrue(self.link_exits('ip6tnl98')) + self.assertTrue(self.link_exits('ip6tnl97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local 2a00:ffde:4567:edde::4987 dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6tnl ip6ip6 remote 2001:473:fece:cafe::5179 local (?:any|::) dev dummy98') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ip6tnl97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'ip6tnl ip6ip6 remote (?:any|::) local 2a00:ffde:4567:edde::4987 dev dummy98') def test_sit_tunnel(self): - self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-sit-tunnel.netdev', 'sit.network') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-sit-tunnel.netdev', 'sit.network', + '25-sit-tunnel-local-any.netdev', + '25-sit-tunnel-remote-any.netdev') self.start_networkd() self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('sittun99')) + self.assertTrue(self.link_exits('sittun98')) + self.assertTrue(self.link_exits('sittun97')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "sit (?:ip6ip |)remote 10.65.223.239 local 10.65.223.238 dev dummy98") + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "sit (?:ip6ip |)remote 10.65.223.239 local any dev dummy98") + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun97']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "sit (?:ip6ip |)remote any local 10.65.223.238 dev dummy98") def test_isatap_tunnel(self): self.copy_unit_to_networkd_unit_path('12-dummy.netdev', '25-isatap-tunnel.netdev', 'isatap.network') @@ -506,18 +783,35 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertTrue(self.link_exits('dummy98')) self.assertTrue(self.link_exits('sittun99')) + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, '6rd-prefix 2602::/24') + @expectedFailureIfERSPANModuleIsNotAvailable() def test_erspan_tunnel(self): - self.copy_unit_to_networkd_unit_path('25-erspan-tunnel.netdev') + self.copy_unit_to_networkd_unit_path('12-dummy.netdev', 'erspan.network', + '25-erspan-tunnel.netdev', '25-erspan-tunnel-local-any.netdev') self.start_networkd() - self.assertTrue(self.link_exits('erspan-test')) + self.assertTrue(self.link_exits('dummy98')) + self.assertTrue(self.link_exits('erspan99')) + self.assertTrue(self.link_exits('erspan98')) - output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan-test']).rstrip().decode('utf-8') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan99']).rstrip().decode('utf-8') print(output) - self.assertTrue(output, '172.16.1.200') - self.assertTrue(output, '172.16.1.100') - self.assertTrue(output, '101') + self.assertRegex(output, 'erspan remote 172.16.1.100 local 172.16.1.200') + self.assertRegex(output, 'ikey 0.0.0.101') + self.assertRegex(output, 'okey 0.0.0.101') + self.assertRegex(output, 'iseq') + self.assertRegex(output, 'oseq') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'erspan98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'erspan remote 172.16.1.100 local any') + self.assertRegex(output, '102') + self.assertRegex(output, 'ikey 0.0.0.102') + self.assertRegex(output, 'okey 0.0.0.102') + self.assertRegex(output, 'iseq') + self.assertRegex(output, 'oseq') def test_tunnel_independent(self): self.copy_unit_to_networkd_unit_path('25-ipip-tunnel-independent.netdev') @@ -525,8 +819,45 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertTrue(self.link_exits('ipiptun99')) + @expectedFailureIfModuleIsNotAvailable('fou') + def test_fou(self): + # The following redundant check is necessary for CentOS CI. + # Maybe, error handling in lookup_id() in sd-netlink/generic-netlink.c needs to be updated. + self.assertTrue(is_module_available('fou')) + + self.copy_unit_to_networkd_unit_path('25-fou-ipproto-ipip.netdev', '25-fou-ipproto-gre.netdev', + '25-fou-ipip.netdev', '25-fou-sit.netdev', + '25-fou-gre.netdev', '25-fou-gretap.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('ipiptun96')) + self.assertTrue(self.link_exits('sittun96')) + self.assertTrue(self.link_exits('gretun96')) + self.assertTrue(self.link_exits('gretap96')) + + output = subprocess.check_output(['ip', 'fou', 'show']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'port 55555 ipproto 4') + self.assertRegex(output, 'port 55556 ipproto 47') + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'ipiptun96']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'sittun96']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55555') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretun96']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'encap fou encap-sport 1001 encap-dport 55556') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'gretap96']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'encap fou encap-sport auto encap-dport 55556') + + subprocess.call(['ip', 'fou', 'del', 'port', '55555']) + subprocess.call(['ip', 'fou', 'del', 'port', '55556']) + def test_vxlan(self): - self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network','11-dummy.netdev') + self.copy_unit_to_networkd_unit_path('25-vxlan.netdev', 'vxlan.network', '11-dummy.netdev') self.start_networkd() self.assertTrue(self.link_exits('vxlan99')) @@ -544,22 +875,103 @@ class NetworkdNetDevTests(unittest.TestCase, Utilities): self.assertRegex(output, 'remcsumrx') self.assertRegex(output, 'gbp') +class NetworkdL2TPTests(unittest.TestCase, Utilities): + + links =[ + 'l2tp-ses1', + 'l2tp-ses2', + 'l2tp-ses3', + 'l2tp-ses4', + 'test1'] + + units = [ + '11-dummy.netdev', + '25-l2tp-dummy.network', + '25-l2tp-ip.netdev', + '25-l2tp-udp.netdev'] + + l2tp_tunnel_ids = [ '10' ] + + def setUp(self): + self.l2tp_tunnel_remove(self.l2tp_tunnel_ids) + self.link_remove(self.links) + + def tearDown(self): + self.l2tp_tunnel_remove(self.l2tp_tunnel_ids) + self.link_remove(self.links) + self.remove_unit_from_networkd_path(self.units) + + @expectedFailureIfModuleIsNotAvailable('l2tp_eth') + def test_l2tp_udp(self): + self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-udp.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('test1')) + self.assertTrue(self.link_exits('l2tp-ses1')) + self.assertTrue(self.link_exits('l2tp-ses2')) + + output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "Tunnel 10, encap UDP") + self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101") + self.assertRegex(output, "Peer tunnel 11") + self.assertRegex(output, "UDP source / dest ports: 3000/4000") + self.assertRegex(output, "UDP checksum: enabled") + + output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '15']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "Session 15 in tunnel 10") + self.assertRegex(output, "Peer session 16, tunnel 11") + self.assertRegex(output, "interface name: l2tp-ses1") + + output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '17']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "Session 17 in tunnel 10") + self.assertRegex(output, "Peer session 18, tunnel 11") + self.assertRegex(output, "interface name: l2tp-ses2") + + @expectedFailureIfModuleIsNotAvailable('l2tp_ip') + def test_l2tp_ip(self): + self.copy_unit_to_networkd_unit_path('11-dummy.netdev', '25-l2tp-dummy.network', '25-l2tp-ip.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('test1')) + self.assertTrue(self.link_exits('l2tp-ses3')) + self.assertTrue(self.link_exits('l2tp-ses4')) + + output = subprocess.check_output(['ip', 'l2tp', 'show', 'tunnel', 'tunnel_id', '10']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "Tunnel 10, encap IP") + self.assertRegex(output, "From 192.168.30.100 to 192.168.30.101") + self.assertRegex(output, "Peer tunnel 12") + + output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '25']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "Session 25 in tunnel 10") + self.assertRegex(output, "Peer session 26, tunnel 12") + self.assertRegex(output, "interface name: l2tp-ses3") + + output = subprocess.check_output(['ip', 'l2tp', 'show', 'session', 'tid', '10', 'session_id', '27']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, "Session 27 in tunnel 10") + self.assertRegex(output, "Peer session 28, tunnel 12") + self.assertRegex(output, "interface name: l2tp-ses4") + class NetworkdNetWorkTests(unittest.TestCase, Utilities): links = [ 'bond199', 'dummy98', + 'dummy99', '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-address-preferred-lifetime-zero-ipv6.network', + '25-address-static.network', + '25-bind-carrier.network', '25-bond-active-backup-slave.netdev', '25-fibrule-invert.network', '25-fibrule-port-range.network', @@ -568,17 +980,13 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): '25-link-local-addressing-no.network', '25-link-local-addressing-yes.network', '25-link-section-unmanaged.network', - '25-route-gateway.network', - '25-route-gateway-on-link.network', '25-route-ipv6-src.network', - '25-route-reverse-order.network', - '25-route-section.network', - '25-route-tcp-window-settings.network', - '25-route-type.network', + '25-route-static.network', + '25-sysctl-disable-ipv6.network', '25-sysctl.network', 'configure-without-carrier.network', - 'routing-policy-rule.network', - 'test-static.network'] + 'routing-policy-rule-dummy98.network', + 'routing-policy-rule-test1.network'] def setUp(self): self.link_remove(self.links) @@ -587,17 +995,57 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): 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') + def test_address_static(self): + self.copy_unit_to_networkd_unit_path('25-address-static.network', '12-dummy.netdev') + self.start_networkd(0) + + self.wait_online(['dummy98:routable']) + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 10.1.2.3/16 brd 10.1.255.255 scope global dummy98') + self.assertRegex(output, 'inet 10.1.2.4/16 brd 10.1.255.255 scope global secondary dummy98') + self.assertRegex(output, 'inet 10.2.2.4/16 brd 10.2.255.255 scope global dummy98') + + # invalid sections + self.assertNotRegex(output, '10.10.0.1/16') + self.assertNotRegex(output, '10.10.0.2/16') + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '32']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet 10.3.2.3/16 brd 10.3.255.255 scope global 32') + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '33']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet 10.4.2.3 peer 10.4.2.4/16 scope global 33') + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '34']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet 192.168.[0-9]*.1/24 brd 192.168.[0-9]*.255 scope global 34') + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dev', 'dummy98', 'label', '35']).rstrip().decode('utf-8') + self.assertRegex(output, 'inet 172.[0-9]*.0.1/16 brd 172.[0-9]*.255.255 scope global 35') + + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet6 2001:db8:0:f101::15/64 scope global') + self.assertRegex(output, 'inet6 2001:db8:0:f101::16/64 scope global') + self.assertRegex(output, 'inet6 2001:db8:0:f102::15/64 scope global') + self.assertRegex(output, 'inet6 2001:db8:0:f102::16/64 scope global') + self.assertRegex(output, 'inet6 2001:db8:0:f103::20 peer 2001:db8:0:f103::10/128 scope global') + self.assertRegex(output, 'inet6 fd[0-9a-f:]*1/64 scope global') + + def test_address_preferred_lifetime_zero_ipv6(self): + self.copy_unit_to_networkd_unit_path('25-address-preferred-lifetime-zero-ipv6.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, '192.168.0.15') - self.assertRegex(output, '192.168.0.1') - self.assertRegex(output, 'routable') + self.assertRegex(output, 'State: routable \(configuring\)') + + 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_configure_without_carrier(self): self.copy_unit_to_networkd_unit_path('configure-without-carrier.network', '11-dummy.netdev') @@ -611,30 +1059,11 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): 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.copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev') + + subprocess.call(['ip', 'rule', 'del', 'table', '7']) + self.start_networkd() self.assertTrue(self.link_exits('test1')) @@ -650,9 +1079,37 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): subprocess.call(['ip', 'rule', 'del', 'table', '7']) + def test_routing_policy_rule_issue_11280(self): + self.copy_unit_to_networkd_unit_path('routing-policy-rule-test1.network', '11-dummy.netdev', + 'routing-policy-rule-dummy98.network', '12-dummy.netdev') + + subprocess.call(['ip', 'rule', 'del', 'table', '7']) + subprocess.call(['ip', 'rule', 'del', 'table', '8']) + + for trial in range(3): + # Remove state files only first time + self.start_networkd(remove_state_files=(trial == 0)) + + self.assertTrue(self.link_exits('test1')) + self.assertTrue(self.link_exits('dummy98')) + + output = subprocess.check_output(['ip', 'rule', 'list', 'table', '7']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, '111: from 192.168.100.18 tos (?:0x08|throughput) iif test1 oif test1 lookup 7') + + output = subprocess.check_output(['ip', 'rule', 'list', 'table', '8']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, '112: from 192.168.101.18 tos (?:0x08|throughput) iif dummy98 oif dummy98 lookup 8') + + subprocess.call(['ip', 'rule', 'del', 'table', '7']) + subprocess.call(['ip', 'rule', 'del', 'table', '8']) + @expectedFailureIfRoutingPolicyPortRangeIsNotAvailable() def test_routing_policy_rule_port_range(self): self.copy_unit_to_networkd_unit_path('25-fibrule-port-range.network', '11-dummy.netdev') + + subprocess.call(['ip', 'rule', 'del', 'table', '7']) + self.start_networkd() self.assertTrue(self.link_exits('test1')) @@ -671,6 +1128,9 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): @expectedFailureIfRoutingPolicyIPProtoIsNotAvailable() def test_routing_policy_rule_invert(self): self.copy_unit_to_networkd_unit_path('25-fibrule-invert.network', '11-dummy.netdev') + + subprocess.call(['ip', 'rule', 'del', 'table', '7']) + self.start_networkd() self.assertTrue(self.link_exits('test1')) @@ -684,128 +1144,47 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): subprocess.call(['ip', 'rule', 'del', 'table', '7']) - def test_address_peer(self): - self.copy_unit_to_networkd_unit_path('25-address-section.network', '12-dummy.netdev') - self.start_networkd() + def test_route_static(self): + self.copy_unit_to_networkd_unit_path('25-route-static.network', '12-dummy.netdev') + self.start_networkd(0) - 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 peer 10.2.3.5/16 scope global 32') - self.assertRegex(output, 'inet 10.6.7.8/16 brd 10.6.255.255 scope global 33') - self.assertRegex(output, 'inet6 2001:db8::20 peer 2001:db8::10/128 scope global') - - output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') - print(output) - self.assertRegex(output, 'State: routable \(configured\)') - - 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')) + self.wait_online(['dummy98:routable']) 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') + self.assertRegex(output, '2001:1234:5:8fff:ff:ff:ff:ff proto static') + self.assertRegex(output, '2001:1234:5:8f63::1 proto kernel') - 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() + output = subprocess.check_output(['ip', '-6', 'route', 'show', 'dev', 'dummy98', 'default']).rstrip().decode('utf-8') + self.assertRegex(output, 'default via 2001:1234:5:8fff:ff:ff:ff:ff proto static metric 1024 pref medium') - self.assertTrue(self.link_exits('dummy98')) - - output = subprocess.check_output(['ip', 'route', 'list']).rstrip().decode('utf-8') + output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98']).rstrip().decode('utf-8') print(output) - self.assertRegex(output, 'blackhole') - self.assertRegex(output, 'unreachable') - self.assertRegex(output, 'prohibit') + self.assertRegex(output, '149.10.124.48/28 proto kernel scope link src 149.10.124.58') + self.assertRegex(output, '149.10.124.64 proto static scope link') + self.assertRegex(output, '192.168.1.1 proto static initcwnd 20') + self.assertRegex(output, '192.168.1.2 proto static initrwnd 30') + + output = subprocess.check_output(['ip', '-4', 'route', 'show', 'dev', 'dummy98', 'default']).rstrip().decode('utf-8') + self.assertRegex(output, 'default via 149.10.125.65 proto static onlink') + self.assertRegex(output, 'default via 149.10.124.64 proto static') + + output = subprocess.check_output(['ip', 'route', 'show', 'type', 'blackhole']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'blackhole 202.54.1.2 proto static') + + output = subprocess.check_output(['ip', 'route', 'show', 'type', 'unreachable']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'unreachable 202.54.1.3 proto static') + + output = subprocess.check_output(['ip', 'route', 'show', 'type', 'prohibit']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'prohibit 202.54.1.4 proto static') 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_route_ipv6_src_route(self): # a dummy device does not make the addresses go through tentative state, so we # reuse a bond from an earlier test, which does make the addresses go through @@ -866,13 +1245,12 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): def test_link_local_addressing(self): self.copy_unit_to_networkd_unit_path('25-link-local-addressing-yes.network', '11-dummy.netdev', '25-link-local-addressing-no.network', '12-dummy.netdev') - self.start_networkd() + self.start_networkd(0) + self.wait_online(['test1:degraded', 'dummy98:carrier']) self.assertTrue(self.link_exits('test1')) self.assertTrue(self.link_exits('dummy98')) - time.sleep(10) - output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'test1']).rstrip().decode('utf-8') print(output) self.assertRegex(output, 'inet .* scope link') @@ -918,14 +1296,15 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): test1_addr_gen_mode = '0' if os.path.exists(os.path.join(os.path.join(network_sysctl_ipv6_path, 'test1'), 'addr_gen_mode')): - self.assertEqual(self.read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), '0') + self.assertEqual(self.read_ipv6_sysctl_attr('test1', 'addr_gen_mode'), test1_addr_gen_mode) if os.path.exists(os.path.join(os.path.join(network_sysctl_ipv6_path, 'dummy98'), 'addr_gen_mode')): self.assertEqual(self.read_ipv6_sysctl_attr('dummy98', 'addr_gen_mode'), '1') def test_sysctl(self): self.copy_unit_to_networkd_unit_path('25-sysctl.network', '12-dummy.netdev') - self.start_networkd() + self.start_networkd(0) + self.wait_online(['dummy98:degraded']) self.assertTrue(self.link_exits('dummy98')) @@ -937,6 +1316,229 @@ class NetworkdNetWorkTests(unittest.TestCase, Utilities): self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'forwarding'),'1') self.assertEqual(self.read_ipv4_sysctl_attr('dummy98', 'proxy_arp'), '1') + def test_sysctl_disable_ipv6(self): + self.copy_unit_to_networkd_unit_path('25-sysctl-disable-ipv6.network', '12-dummy.netdev') + + print('## Disable ipv6') + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.all.disable_ipv6=1']), 0) + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.default.disable_ipv6=1']), 0) + + self.start_networkd(0) + self.wait_online(['dummy98:routable']) + + self.assertTrue(self.link_exits('dummy98')) + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertEqual(output, '') + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + + print('## Enable ipv6') + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.all.disable_ipv6=0']), 0) + self.assertEqual(subprocess.call(['sysctl', 'net.ipv6.conf.default.disable_ipv6=0']), 0) + + self.start_networkd(0) + self.wait_online(['dummy98:routable']) + + self.assertTrue(self.link_exits('dummy98')) + + output = subprocess.check_output(['ip', '-4', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 10.2.3.4/16 brd 10.2.255.255 scope global dummy98') + output = subprocess.check_output(['ip', '-6', 'address', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet6 .* scope link') + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + def test_bind_carrier(self): + self.copy_unit_to_networkd_unit_path('25-bind-carrier.network', '11-dummy.netdev') + self.start_networkd() + + self.assertTrue(self.link_exits('test1')) + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + time.sleep(2) + output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'UP,LOWER_UP') + self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy99', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy99', 'up']), 0) + time.sleep(2) + output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'UP,LOWER_UP') + self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + time.sleep(2) + output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'UP,LOWER_UP') + self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy99']), 0) + time.sleep(2) + output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertNotRegex(output, 'UP,LOWER_UP') + self.assertRegex(output, 'DOWN') + self.assertNotRegex(output, '192.168.10') + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: off \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + time.sleep(2) + output = subprocess.check_output(['ip', 'address', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'UP,LOWER_UP') + self.assertRegex(output, 'inet 192.168.10.30/24 brd 192.168.10.255 scope global test1') + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + +class NetworkdNetWorkBondTests(unittest.TestCase, Utilities): + links = [ + 'bond199', + 'bond99', + '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-bond-active-backup-slave.netdev', + '25-bond.netdev', + 'bond99.network', + 'bond-slave.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_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_bond_operstate(self): + self.copy_unit_to_networkd_unit_path('25-bond.netdev', '11-dummy.netdev', '12-dummy.netdev', + 'bond99.network','bond-slave.network') + self.start_networkd() + + self.assertTrue(self.link_exits('bond99')) + self.assertTrue(self.link_exits('dummy98')) + self.assertTrue(self.link_exits('test1')) + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'SLAVE,UP,LOWER_UP') + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'SLAVE,UP,LOWER_UP') + + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'MASTER,UP,LOWER_UP') + + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0) + time.sleep(2) + + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: off \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: degraded-carrier \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + time.sleep(2) + + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'down']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'test1', 'down']), 0) + time.sleep(5) + + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: off \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: off \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bond99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: no-carrier \(configured\)') + class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): links = [ 'bridge99', @@ -949,6 +1551,7 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): '26-bridge.netdev', '26-bridge-slave-interface-1.network', '26-bridge-slave-interface-2.network', + 'bridge99-ignore-carrier-loss.network', 'bridge99.network'] def setUp(self): @@ -980,20 +1583,131 @@ class NetworkdNetWorkBridgeTests(unittest.TestCase, Utilities): 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') + self.assertRegex(output, '192.168.0.15/24') 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_flood'), '0') self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'multicast_fast_leave'), '1') + if (os.path.exists('/sys/devices/virtual/net/bridge99/lower_dummy98/brport/neigh_suppress')): + self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'neigh_suppress'), '1') + self.assertEqual(self.read_bridge_port_attr('bridge99', 'dummy98', 'learning'), '0') # 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') + output = subprocess.check_output(['networkctl', 'status', 'test1']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0) + time.sleep(1) + + output = subprocess.check_output(['ip', 'addr', 'show', 'bridge99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, '192.168.0.16/24') + + output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'test1']), 0) + time.sleep(3) + + output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: degraded-carrier \(configured\)') + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + time.sleep(3) + + output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: no-carrier \(configured\)') + + output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'NO-CARRIER') + self.assertNotRegex(output, '192.168.0.15/24') + self.assertNotRegex(output, '192.168.0.16/24') + + def test_bridge_ignore_carrier_loss(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-ignore-carrier-loss.network') + + subprocess.call(['ip', 'rule', 'del', 'table', '100']) + + self.start_networkd() + + self.assertTrue(self.link_exits('dummy98')) + self.assertTrue(self.link_exits('test1')) + self.assertTrue(self.link_exits('bridge99')) + + self.assertEqual(subprocess.call(['ip', 'address', 'add', '192.168.0.16/24', 'dev', 'bridge99']), 0) + time.sleep(1) + + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'test1']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + time.sleep(3) + + output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'NO-CARRIER') + self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99') + self.assertRegex(output, 'inet 192.168.0.16/24 scope global secondary bridge99') + + subprocess.call(['ip', 'rule', 'del', 'table', '100']) + + def test_bridge_ignore_carrier_loss_frequent_loss_and_gain(self): + self.copy_unit_to_networkd_unit_path('26-bridge.netdev', '26-bridge-slave-interface-1.network', + 'bridge99-ignore-carrier-loss.network') + + subprocess.call(['ip', 'rule', 'del', 'table', '100']) + + self.start_networkd() + + self.assertTrue(self.link_exits('bridge99')) + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'del', 'dummy98']), 0) + + self.assertEqual(subprocess.call(['ip', 'link', 'add', 'dummy98', 'type', 'dummy']), 0) + self.assertEqual(subprocess.call(['ip', 'link', 'set', 'dummy98', 'up']), 0) + + time.sleep(3) + + output = subprocess.check_output(['ip', 'address', 'show', 'bridge99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'inet 192.168.0.15/24 brd 192.168.0.255 scope global bridge99') + + output = subprocess.check_output(['networkctl', 'status', 'bridge99']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: routable \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'dummy98']).rstrip().decode('utf-8') + self.assertRegex(output, 'State: enslaved \(configured\)') + + output = subprocess.check_output(['ip', 'rule', 'list', 'table', '100']).rstrip().decode('utf-8') + print(output) + self.assertEqual(output, '0: from all to 8.8.8.8 lookup 100') + + subprocess.call(['ip', 'rule', 'del', 'table', '100']) + class NetworkdNetWorkLLDPTests(unittest.TestCase, Utilities): links = ['veth99'] @@ -1106,12 +1820,16 @@ class NetworkdNetworkDHCPServerTests(unittest.TestCase, Utilities): class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): links = [ 'dummy98', - 'veth99'] + 'veth99', + 'vrf99'] units = [ '25-veth.netdev', + '25-vrf.netdev', + '25-vrf.network', 'dhcp-client-anonymize.network', 'dhcp-client-critical-connection.network', + 'dhcp-client-gateway-onlink-implicit.network', 'dhcp-client-ipv4-dhcp-settings.network', 'dhcp-client-ipv4-only-ipv6-disabled.network', 'dhcp-client-ipv4-only.network', @@ -1120,6 +1838,7 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): 'dhcp-client-listen-port.network', 'dhcp-client-route-metric.network', 'dhcp-client-route-table.network', + 'dhcp-client-vrf.network', 'dhcp-client.network', 'dhcp-server-veth-peer.network', 'dhcp-v4-server-veth-peer.network', @@ -1184,16 +1903,27 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): self.start_dnsmasq() + print('## ip address show dev veth99') 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') + # issue #8726 + print('## ip route show table main dev veth99') + output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99']).rstrip().decode('utf-8') print(output) - self.assertRegex(output, 'default.*dev veth99 proto dhcp') + self.assertNotRegex(output, 'proto dhcp') + print('## ip route show table 211 dev veth99') + output = subprocess.check_output(['ip', 'route', 'show', 'table', '211', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'default via 192.168.5.1 proto dhcp') + self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 proto dhcp') + self.assertRegex(output, '192.168.5.1 proto dhcp scope link') + + print('## dnsmasq log') 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')) @@ -1333,6 +2063,78 @@ class NetworkdNetworkDHCPClientTests(unittest.TestCase, Utilities): self.assertRegex(output, '2600::') self.assertRegex(output, 'valid_lft forever preferred_lft forever') + @expectedFailureIfModuleIsNotAvailable('vrf') + def test_dhcp_client_vrf(self): + self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', 'dhcp-client-vrf.network', + '25-vrf.netdev', '25-vrf.network') + self.start_networkd() + + self.assertTrue(self.link_exits('veth99')) + self.assertTrue(self.link_exits('vrf99')) + + self.start_dnsmasq() + + print('## ip -d link show dev vrf99') + output = subprocess.check_output(['ip', '-d', 'link', 'show', 'dev', 'vrf99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'vrf table 42') + + print('## ip address show vrf vrf99') + output_ip_vrf = subprocess.check_output(['ip', 'address', 'show', 'vrf', 'vrf99']).rstrip().decode('utf-8') + print(output_ip_vrf) + + print('## ip address show dev veth99') + output = subprocess.check_output(['ip', 'address', 'show', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertEqual(output, output_ip_vrf) + self.assertRegex(output, 'inet 169.254.[0-9]*.[0-9]*/16 brd 169.254.255.255 scope link veth99') + self.assertRegex(output, 'inet 192.168.5.[0-9]*/24 brd 192.168.5.255 scope global dynamic veth99') + self.assertRegex(output, 'inet6 2600::[0-9a-f]*/128 scope global dynamic noprefixroute') + self.assertRegex(output, 'inet6 .* scope link') + + print('## ip route show vrf vrf99') + output = subprocess.check_output(['ip', 'route', 'show', 'vrf', 'vrf99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'default via 192.168.5.1 dev veth99 proto dhcp src 192.168.5.') + self.assertRegex(output, 'default dev veth99 proto static scope link') + self.assertRegex(output, '169.254.0.0/16 dev veth99 proto kernel scope link src 169.254') + self.assertRegex(output, '192.168.5.0/24 dev veth99 proto kernel scope link src 192.168.5') + self.assertRegex(output, '192.168.5.0/24 via 192.168.5.5 dev veth99 proto dhcp') + self.assertRegex(output, '192.168.5.1 dev veth99 proto dhcp scope link src 192.168.5') + + print('## ip route show table main dev veth99') + output = subprocess.check_output(['ip', 'route', 'show', 'table', 'main', 'dev', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertEqual(output, '') + + output = subprocess.check_output(['networkctl', 'status', 'vrf99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: carrier \(configured\)') + + output = subprocess.check_output(['networkctl', 'status', 'veth99']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'State: routable \(configured\)') + + def test_dhcp_client_gateway_onlink_implicit(self): + self.copy_unit_to_networkd_unit_path('25-veth.netdev', 'dhcp-server-veth-peer.network', + 'dhcp-client-gateway-onlink-implicit.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') + + output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '10.0.0.0/8']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'onlink') + output = subprocess.check_output(['ip', 'route', 'list', 'dev', 'veth99', '192.168.100.0/24']).rstrip().decode('utf-8') + print(output) + self.assertRegex(output, 'onlink') + 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 957cda541..122359e37 100755 --- a/test/udev-test.pl +++ b/test/udev-test.pl @@ -39,11 +39,17 @@ for (my $i = 1; $i <= 10000; ++$i) { $rules_10k_tags .= 'KERNEL=="sda", TAG+="test' . $i . "\"\n"; } +my $rules_10k_tags_continuation = "KERNEL==\"sda\", \\\n"; +for (my $i = 1; $i < 10000; ++$i) { + $rules_10k_tags_continuation .= 'TAG+="test' . $i . "\",\\\n"; +} +$rules_10k_tags_continuation .= "TAG+=\"test10000\"\\n"; + my @tests = ( { desc => "no rules", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "sda" , + exp_name => "sda", exp_rem_error => "yes", rules => < "label test of scsi disc", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + exp_name => "boot_disk", rules => < "label test of scsi disc", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + exp_name => "boot_disk", rules => < "label test of scsi disc", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "boot_disk" , + exp_name => "boot_disk", rules => < "label test of scsi partition", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_name => "boot_disk1", rules => < "label test of pattern match", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_name => "boot_disk1", rules => < "label test of multiple sysfs files", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_name => "boot_disk1", rules => < "label test of max sysfs files (skip invalid rule)", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda1", - exp_name => "boot_disk1" , + exp_name => "boot_disk1", rules => < "catch device by *", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_name => "modem/0", rules => < "catch device by * - take 2", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_name => "modem/0", rules => < "catch device by ?", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_name => "modem/0", rules => < "catch device by character class", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem/0" , + exp_name => "modem/0", rules => < "replace kernel name", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "Handle comment lines in config file (and replace kernel name)", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "Handle comment lines in config file with whitespace (and replace kernel name)", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "Handle whitespace only lines (and replace kernel name)", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "whitespace" , + exp_name => "whitespace", rules => < "Handle empty lines in config file (and replace kernel name)", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "Handle backslashed multi lines in config file (and replace kernel name)", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "Handle stupid backslashed multi lines in config file (and replace kernel name)", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "subdirectory handling", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "sub/direct/ory/modem" , + exp_name => "sub/direct/ory/modem", rules => < "parent device name match of scsi partition", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "first_disk5" , + exp_name => "first_disk5", rules => < "test substitution chars", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0" , + exp_name => "Major:8:minor:5:kernelnumber:5:id:0:0:0:0", rules => < "sustitution of sysfs value (%s{file})", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "disk-ATA-sda" , + exp_name => "disk-ATA-sda", rules => < "program result substitution", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "special-device-5" , - not_exp_name => "not" , + exp_name => "special-device-5", + not_exp_name => "not", rules => < "program result substitution (newline removal)", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "newline_removed" , + exp_name => "newline_removed", rules => < "program result substitution", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "test-0:0:0:0" , + exp_name => "test-0:0:0:0", rules => < "program with lots of arguments", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo9" , + exp_name => "foo9", rules => < "program with subshell", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "bar9" , + exp_name => "bar9", rules => < "program arguments combined with apostrophes", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo7" , + exp_name => "foo7", rules => < "program arguments combined with escaped double quotes, part 1", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + exp_name => "foo2", rules => < "program arguments combined with escaped double quotes, part 2", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + exp_name => "foo2", rules => < "program arguments combined with escaped double quotes, part 3", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "foo2" , + exp_name => "foo2", rules => < "characters before the %c{N} substitution", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "my-foo9" , + exp_name => "my-foo9", rules => < "substitute the second to last argument", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "my-foo8" , + exp_name => "my-foo8", rules => < "test substitution by variable name 3", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "850:0:0:05" , + exp_name => "850:0:0:05", rules => < "test substitution by variable name 4", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "855" , + exp_name => "855", rules => < "test substitution by variable name 5", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda/sda5", - exp_name => "8550:0:0:0" , + exp_name => "8550:0:0:0", rules => < "non matching SUBSYSTEMS", devpath => "/devices/virtual/tty/console", - exp_name => "TTY" , + exp_name => "TTY", rules => < "ATTRS match", devpath => "/devices/virtual/tty/console", - exp_name => "foo" , + exp_name => "foo", rules => < "ATTR (empty file)", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "empty" , + exp_name => "empty", rules => < "ATTR (non-existent file)", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "non-existent" , + exp_name => "non-existent", rules => < "program and bus type match", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", - exp_name => "scsi-0:0:0:0" , + exp_name => "scsi-0:0:0:0", rules => < "sysfs parent hierarchy", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "modem" , + exp_name => "modem", rules => < "name test with ! in the name", devpath => "/devices/virtual/block/fake!blockdev0", - exp_name => "is/a/fake/blockdev0" , + exp_name => "is/a/fake/blockdev0", rules => < "name test with ! in the name, but no matching rule", devpath => "/devices/virtual/block/fake!blockdev0", - exp_name => "fake/blockdev0" , + exp_name => "fake/blockdev0", exp_rem_error => "yes", rules => < "KERNELS wildcard partial 2", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", exp_name => "scsi-0:0:0:0", - rules => < < "/devices/virtual/misc/misc-fake1", exp_name => "node", exp_majorminor => "4095:1", - rules => < < "multiple symlinks with a lot of s p a c e s", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", exp_name => "one", - not_exp_name => " ", + not_exp_name => " ", rules => < "multiple symlinks", devpath => "/devices/pci0000:00/0000:00:1d.7/usb5/5-2/5-2:1.0/tty/ttyACM0", - exp_name => "second-0" , + exp_name => "second-0", rules => < "symlink name '.'", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", exp_name => ".", - exp_add_error => "yes", - exp_rem_error => "yes", + exp_add_error => "yes", + exp_rem_error => "yes", rules => < "symlink node to itself", devpath => "/devices/virtual/tty/tty0", exp_name => "link", - exp_add_error => "yes", - exp_rem_error => "yes", - option => "clean", + exp_add_error => "yes", + exp_rem_error => "yes", + option => "clean", rules => < "add and match tag", devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", exp_name => "found", - not_exp_name => "bad" , + not_exp_name => "bad", rules => < "found", rules => $rules_10k_tags . < "continuations", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", + not_exp_name => "bad", + rules => $rules_10k_tags_continuation . < "continuations with empty line", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", + not_exp_name => "bad", + rules => < "continuations with white only line", + devpath => "/devices/pci0000:00/0000:00:1f.2/host0/target0:0:0/0:0:0:0/block/sda", + exp_name => "found", + not_exp_name => "bad", + rules => <>/etc/apt/sources.list" $DOCKER_EXEC apt-get -y update $DOCKER_EXEC apt-get -y build-dep systemd diff --git a/units/dev-mqueue.mount b/units/dev-mqueue.mount index be32433d6..0114ad31f 100644 --- a/units/dev-mqueue.mount +++ b/units/dev-mqueue.mount @@ -20,3 +20,4 @@ ConditionCapability=CAP_SYS_ADMIN What=mqueue Where=/dev/mqueue Type=mqueue +Options=nosuid,nodev,noexec diff --git a/units/meson.build b/units/meson.build index d69508467..a5610506d 100644 --- a/units/meson.build +++ b/units/meson.build @@ -22,7 +22,7 @@ units = [ ['halt.target', ''], ['hibernate.target', 'ENABLE_HIBERNATE'], ['hybrid-sleep.target', 'ENABLE_HIBERNATE'], - ['suspend-then-hibernate.target', 'ENABLE_HIBERNATE'], + ['suspend-then-hibernate.target', 'ENABLE_HIBERNATE'], ['initrd-fs.target', ''], ['initrd-root-device.target', ''], ['initrd-root-fs.target', ''], @@ -34,8 +34,7 @@ units = [ ['local-fs-pre.target', ''], ['local-fs.target', ''], ['machine.slice', 'ENABLE_MACHINED'], - ['machines.target', 'ENABLE_MACHINED', - join_paths(pkgsysconfdir, 'system/multi-user.target.wants/')], + ['machines.target', 'ENABLE_MACHINED'], ['multi-user.target', '', 'runlevel2.target runlevel3.target runlevel4.target'], ['network-online.target', ''], @@ -52,11 +51,9 @@ units = [ ['proc-sys-fs-binfmt_misc.mount', 'ENABLE_BINFMT'], ['reboot.target', '', 'runlevel6.target ctrl-alt-del.target'], - ['remote-cryptsetup.target', 'HAVE_LIBCRYPTSETUP', - join_paths(pkgsysconfdir, 'system/multi-user.target.wants/')], + ['remote-cryptsetup.target', 'HAVE_LIBCRYPTSETUP'], ['remote-fs-pre.target', ''], - ['remote-fs.target', '', - join_paths(pkgsysconfdir, 'system/multi-user.target.wants/')], + ['remote-fs.target', ''], ['rescue.target', '', 'runlevel1.target'], ['rpcbind.target', ''], @@ -97,8 +94,7 @@ units = [ 'sockets.target.wants/'], ['systemd-journald.socket', '', 'sockets.target.wants/'], - ['systemd-networkd.socket', 'ENABLE_NETWORKD', - join_paths(pkgsysconfdir, 'system/sockets.target.wants/')], + ['systemd-networkd.socket', 'ENABLE_NETWORKD'], ['systemd-poweroff.service', ''], ['systemd-reboot.service', ''], ['systemd-rfkill.socket', 'ENABLE_RFKILL'], @@ -108,6 +104,7 @@ units = [ 'sockets.target.wants/'], ['systemd-udevd-kernel.socket', '', 'sockets.target.wants/'], + ['time-set.target', ''], ['time-sync.target', ''], ['timers.target', ''], ['tmp.mount', '', @@ -175,22 +172,16 @@ in_units = [ 'dbus-org.freedesktop.machine1.service'], ['systemd-modules-load.service', 'HAVE_KMOD', 'sysinit.target.wants/'], - ['systemd-networkd.service', 'ENABLE_NETWORKD', - join_paths(pkgsysconfdir, 'system/dbus-org.freedesktop.network1.service') + ' ' + - join_paths(pkgsysconfdir, 'system/multi-user.target.wants/')], - ['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD', - join_paths(pkgsysconfdir, 'system/network-online.target.wants/')], + ['systemd-networkd.service', 'ENABLE_NETWORKD'], + ['systemd-networkd-wait-online.service', 'ENABLE_NETWORKD'], ['systemd-nspawn@.service', ''], ['systemd-portabled.service', 'ENABLE_PORTABLED', 'dbus-org.freedesktop.portable1.service'], ['systemd-quotacheck.service', 'ENABLE_QUOTACHECK'], ['systemd-random-seed.service', 'ENABLE_RANDOMSEED', 'sysinit.target.wants/'], - ['systemd-remount-fs.service', '', - 'local-fs.target.wants/'], - ['systemd-resolved.service', 'ENABLE_RESOLVE', - join_paths(pkgsysconfdir, 'system/dbus-org.freedesktop.resolve1.service') + ' ' + - join_paths(pkgsysconfdir, 'system/multi-user.target.wants/')], + ['systemd-remount-fs.service', ''], + ['systemd-resolved.service', 'ENABLE_RESOLVE'], ['systemd-rfkill.service', 'ENABLE_RFKILL'], ['systemd-suspend.service', ''], ['systemd-sysctl.service', '', @@ -199,8 +190,7 @@ in_units = [ 'sysinit.target.wants/'], ['systemd-timedated.service', 'ENABLE_TIMEDATED', 'dbus-org.freedesktop.timedate1.service'], - ['systemd-timesyncd.service', 'ENABLE_TIMESYNCD', - join_paths(pkgsysconfdir, 'system/sysinit.target.wants/')], + ['systemd-timesyncd.service', 'ENABLE_TIMESYNCD'], ['systemd-time-wait-sync.service', 'ENABLE_TIMESYNCD'], ['systemd-tmpfiles-clean.service', 'ENABLE_TMPFILES'], ['systemd-tmpfiles-setup-dev.service', 'ENABLE_TMPFILES', @@ -230,8 +220,7 @@ m4_units = [ ['console-getty.service', ''], ['container-getty@.service', ''], ['getty@.service', '', - 'autovt@.service ' + - join_paths(pkgsysconfdir, 'system/getty.target.wants/getty@tty1.service')], + 'autovt@.service '], ['serial-getty@.service', ''], ] diff --git a/units/proc-sys-fs-binfmt_misc.mount b/units/proc-sys-fs-binfmt_misc.mount index 091191e13..66229ec78 100644 --- a/units/proc-sys-fs-binfmt_misc.mount +++ b/units/proc-sys-fs-binfmt_misc.mount @@ -17,3 +17,4 @@ DefaultDependencies=no What=binfmt_misc Where=/proc/sys/fs/binfmt_misc Type=binfmt_misc +Options=nosuid,nodev,noexec diff --git a/units/sys-fs-fuse-connections.mount b/units/sys-fs-fuse-connections.mount index 7e7b05c3a..7bbc342be 100644 --- a/units/sys-fs-fuse-connections.mount +++ b/units/sys-fs-fuse-connections.mount @@ -22,3 +22,4 @@ Before=sysinit.target What=fusectl Where=/sys/fs/fuse/connections Type=fusectl +Options=nosuid,nodev,noexec diff --git a/units/sys-kernel-config.mount b/units/sys-kernel-config.mount index e213ca58b..e6997884d 100644 --- a/units/sys-kernel-config.mount +++ b/units/sys-kernel-config.mount @@ -21,3 +21,4 @@ Before=sysinit.target What=configfs Where=/sys/kernel/config Type=configfs +Options=nosuid,nodev,noexec diff --git a/units/sys-kernel-debug.mount b/units/sys-kernel-debug.mount index 53ce820b8..618270dda 100644 --- a/units/sys-kernel-debug.mount +++ b/units/sys-kernel-debug.mount @@ -20,3 +20,4 @@ Before=sysinit.target What=debugfs Where=/sys/kernel/debug Type=debugfs +Options=nosuid,nodev,noexec diff --git a/units/system-update-cleanup.service b/units/system-update-cleanup.service index 58baab302..d5eca2546 100644 --- a/units/system-update-cleanup.service +++ b/units/system-update-cleanup.service @@ -9,7 +9,7 @@ [Unit] Description=Remove the Offline System Updates symlink -Documentation=man:systemd.special(5) man:systemd.offline-updates(7) +Documentation=man:systemd.special(7) man:systemd.offline-updates(7) After=system-update.target DefaultDependencies=no Conflicts=shutdown.target diff --git a/units/systemd-coredump@.service.in b/units/systemd-coredump@.service.in index ffcb5f36c..afb2ab9d1 100644 --- a/units/systemd-coredump@.service.in +++ b/units/systemd-coredump@.service.in @@ -29,12 +29,14 @@ PrivateNetwork=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectSystem=strict RestrictAddressFamilies=AF_UNIX RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes RuntimeMaxSec=5min StateDirectory=systemd/coredump SystemCallArchitectures=native diff --git a/units/systemd-hostnamed.service.in b/units/systemd-hostnamed.service.in index 9c925e80d..b4f606cf7 100644 --- a/units/systemd-hostnamed.service.in +++ b/units/systemd-hostnamed.service.in @@ -32,6 +32,7 @@ ReadWritePaths=/etc RestrictAddressFamilies=AF_UNIX RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service sethostname diff --git a/units/systemd-importd.service.in b/units/systemd-importd.service.in index 20704a823..38b7d7e94 100644 --- a/units/systemd-importd.service.in +++ b/units/systemd-importd.service.in @@ -20,6 +20,7 @@ KillMode=mixed CapabilityBoundingSet=CAP_CHOWN CAP_FOWNER CAP_FSETID CAP_MKNOD CAP_SETFCAP CAP_SYS_ADMIN CAP_SETPCAP CAP_DAC_OVERRIDE NoNewPrivileges=yes MemoryDenyWriteExecute=yes +ProtectHostname=yes RestrictRealtime=yes RestrictNamespaces=net RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 diff --git a/units/systemd-journal-catalog-update.service.in b/units/systemd-journal-catalog-update.service.in index 6a7bf006c..18b2739ff 100644 --- a/units/systemd-journal-catalog-update.service.in +++ b/units/systemd-journal-catalog-update.service.in @@ -12,7 +12,7 @@ Description=Rebuild Journal Catalog Documentation=man:systemd-journald.service(8) man:journald.conf(5) DefaultDependencies=no Conflicts=shutdown.target -After=local-fs.target +After=local-fs.target systemd-tmpfiles-setup.service Before=sysinit.target shutdown.target systemd-update-done.service ConditionNeedsUpdate=/var diff --git a/units/systemd-journal-gatewayd.service.in b/units/systemd-journal-gatewayd.service.in index ebc8bf9a2..50f774512 100644 --- a/units/systemd-journal-gatewayd.service.in +++ b/units/systemd-journal-gatewayd.service.in @@ -17,11 +17,11 @@ DynamicUser=yes ExecStart=@rootlibexecdir@/systemd-journal-gatewayd LockPersonality=yes MemoryDenyWriteExecute=yes -NoNewPrivileges=yes PrivateDevices=yes PrivateNetwork=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 diff --git a/units/systemd-journal-remote.service.in b/units/systemd-journal-remote.service.in index 29a99aaec..dd6322e62 100644 --- a/units/systemd-journal-remote.service.in +++ b/units/systemd-journal-remote.service.in @@ -23,12 +23,14 @@ PrivateNetwork=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectSystem=strict RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes SystemCallArchitectures=native User=systemd-journal-remote WatchdogSec=3min diff --git a/units/systemd-journal-upload.service.in b/units/systemd-journal-upload.service.in index 92cd4e525..e3800473e 100644 --- a/units/systemd-journal-upload.service.in +++ b/units/systemd-journal-upload.service.in @@ -18,10 +18,10 @@ DynamicUser=yes ExecStart=@rootlibexecdir@/systemd-journal-upload --save-state LockPersonality=yes MemoryDenyWriteExecute=yes -NoNewPrivileges=yes PrivateDevices=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 diff --git a/units/systemd-journald.service.in b/units/systemd-journald.service.in index 4684f095c..fab405502 100644 --- a/units/systemd-journald.service.in +++ b/units/systemd-journald.service.in @@ -28,6 +28,7 @@ RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_NETLINK RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes Sockets=systemd-journald.socket systemd-journald-dev-log.socket systemd-journald-audit.socket StandardOutput=null SystemCallArchitectures=native diff --git a/units/systemd-localed.service.in b/units/systemd-localed.service.in index 01e0703d0..7bca34409 100644 --- a/units/systemd-localed.service.in +++ b/units/systemd-localed.service.in @@ -25,6 +25,7 @@ PrivateNetwork=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectSystem=strict @@ -32,6 +33,7 @@ ReadWritePaths=/etc RestrictAddressFamilies=AF_UNIX RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service diff --git a/units/systemd-logind.service.in b/units/systemd-logind.service.in index 38a7f269a..3eef95c66 100644 --- a/units/systemd-logind.service.in +++ b/units/systemd-logind.service.in @@ -21,18 +21,28 @@ After=dbus.socket [Service] BusName=org.freedesktop.login1 -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 +CapabilityBoundingSet=CAP_SYS_ADMIN CAP_MAC_ADMIN CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE CAP_FOWNER CAP_SYS_TTY_CONFIG CAP_LINUX_IMMUTABLE ExecStart=@rootlibexecdir@/systemd-logind FileDescriptorStoreMax=512 IPAddressDeny=any LockPersonality=yes MemoryDenyWriteExecute=yes NoNewPrivileges=yes +PrivateTmp=yes +ProtectControlGroups=yes +ProtectHome=yes +ProtectHostname=yes +ProtectKernelModules=yes +ProtectSystem=strict +ReadWritePaths=/etc /run Restart=always RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_NETLINK RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes +RuntimeDirectory=systemd/sessions systemd/seats systemd/users systemd/inhibit systemd/shutdown +RuntimeDirectoryPreserve=yes SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service diff --git a/units/systemd-machined.service.in b/units/systemd-machined.service.in index 9f1476814..d6deefea0 100644 --- a/units/systemd-machined.service.in +++ b/units/systemd-machined.service.in @@ -23,6 +23,7 @@ IPAddressDeny=any LockPersonality=yes MemoryDenyWriteExecute=yes NoNewPrivileges=yes +ProtectHostname=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 RestrictRealtime=yes SystemCallArchitectures=native diff --git a/units/systemd-networkd.service.in b/units/systemd-networkd.service.in index 472ef045d..2c74da6f1 100644 --- a/units/systemd-networkd.service.in +++ b/units/systemd-networkd.service.in @@ -34,6 +34,7 @@ RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 AF_PACKET RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes RuntimeDirectory=systemd/netif RuntimeDirectoryPreserve=yes SystemCallArchitectures=native diff --git a/units/systemd-portabled.service.in b/units/systemd-portabled.service.in index a44cdb30a..a8eab94d0 100644 --- a/units/systemd-portabled.service.in +++ b/units/systemd-portabled.service.in @@ -18,6 +18,7 @@ BusName=org.freedesktop.portable1 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 MemoryDenyWriteExecute=yes +ProtectHostname=yes RestrictRealtime=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 SystemCallFilter=@system-service @mount diff --git a/units/systemd-remount-fs.service.in b/units/systemd-remount-fs.service.in index 2e5b75ec0..4f4304d68 100644 --- a/units/systemd-remount-fs.service.in +++ b/units/systemd-remount-fs.service.in @@ -16,7 +16,6 @@ Conflicts=shutdown.target After=systemd-fsck-root.service Before=local-fs-pre.target local-fs.target shutdown.target Wants=local-fs-pre.target -ConditionPathExists=/etc/fstab [Service] Type=oneshot diff --git a/units/systemd-resolved.service.in b/units/systemd-resolved.service.in index 3144b7006..eee5d5ea8 100644 --- a/units/systemd-resolved.service.in +++ b/units/systemd-resolved.service.in @@ -38,6 +38,7 @@ RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes RuntimeDirectory=systemd/resolve RuntimeDirectoryPreserve=yes SystemCallArchitectures=native diff --git a/units/systemd-timedated.service.in b/units/systemd-timedated.service.in index 6d5302419..df546f471 100644 --- a/units/systemd-timedated.service.in +++ b/units/systemd-timedated.service.in @@ -23,6 +23,7 @@ NoNewPrivileges=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectSystem=strict @@ -30,6 +31,7 @@ ReadWritePaths=/etc RestrictAddressFamilies=AF_UNIX RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes SystemCallArchitectures=native SystemCallErrorNumber=EPERM SystemCallFilter=@system-service @clock diff --git a/units/systemd-timesyncd.service.in b/units/systemd-timesyncd.service.in index 03ade45d0..2d8d14f6d 100644 --- a/units/systemd-timesyncd.service.in +++ b/units/systemd-timesyncd.service.in @@ -14,9 +14,9 @@ ConditionCapability=CAP_SYS_TIME ConditionVirtualization=!container DefaultDependencies=no After=systemd-remount-fs.service systemd-sysusers.service -Before=time-sync.target sysinit.target shutdown.target +Before=time-set.target sysinit.target shutdown.target Conflicts=shutdown.target -Wants=time-sync.target +Wants=time-set.target time-sync.target [Service] AmbientCapabilities=CAP_SYS_TIME @@ -29,6 +29,7 @@ PrivateDevices=yes PrivateTmp=yes ProtectControlGroups=yes ProtectHome=yes +ProtectHostname=yes ProtectKernelModules=yes ProtectKernelTunables=yes ProtectSystem=strict @@ -37,6 +38,7 @@ RestartSec=0 RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 RestrictNamespaces=yes RestrictRealtime=yes +RestrictSUIDSGID=yes RuntimeDirectory=systemd/timesync StateDirectory=systemd/timesync SystemCallArchitectures=native diff --git a/units/systemd-tmpfiles-clean.service.in b/units/systemd-tmpfiles-clean.service.in index d1025afad..9e2f04bfe 100644 --- a/units/systemd-tmpfiles-clean.service.in +++ b/units/systemd-tmpfiles-clean.service.in @@ -12,7 +12,7 @@ Description=Cleanup of Temporary Directories Documentation=man:tmpfiles.d(5) man:systemd-tmpfiles(8) DefaultDependencies=no Conflicts=shutdown.target -After=local-fs.target time-sync.target +After=local-fs.target time-set.target Before=shutdown.target [Service] diff --git a/units/systemd-udevd.service.in b/units/systemd-udevd.service.in index 6a3814e5d..e8a76cc01 100644 --- a/units/systemd-udevd.service.in +++ b/units/systemd-udevd.service.in @@ -26,9 +26,11 @@ KillMode=mixed WatchdogSec=3min TasksMax=infinity PrivateMounts=yes +ProtectHostname=yes MemoryDenyWriteExecute=yes -RestrictRealtime=yes RestrictAddressFamilies=AF_UNIX AF_NETLINK AF_INET AF_INET6 +RestrictRealtime=yes +RestrictSUIDSGID=yes SystemCallFilter=@system-service @module @raw-io SystemCallErrorNumber=EPERM SystemCallArchitectures=native diff --git a/units/time-set.target b/units/time-set.target new file mode 100644 index 000000000..6b4003352 --- /dev/null +++ b/units/time-set.target @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=System Time Set +Documentation=man:systemd.special(7) +RefuseManualStart=yes diff --git a/units/time-sync.target b/units/time-sync.target index 1533c7c33..9106502e8 100644 --- a/units/time-sync.target +++ b/units/time-sync.target @@ -11,3 +11,5 @@ Description=System Time Synchronized Documentation=man:systemd.special(7) RefuseManualStart=yes +After=time-set.target +Wants=time-set.target diff --git a/units/tmp.mount b/units/tmp.mount index 742d86385..27bd0f235 100644 --- a/units/tmp.mount +++ b/units/tmp.mount @@ -9,7 +9,8 @@ [Unit] Description=Temporary Directory (/tmp) -Documentation=man:hier(7) +Documentation=https://systemd.io/TEMPORARY_DIRECTORIES +Documentation=man:file-hierarchy(7) Documentation=https://www.freedesktop.org/wiki/Software/systemd/APIFileSystems ConditionPathIsSymbolicLink=!/tmp DefaultDependencies=no diff --git a/units/usb-gadget.target b/units/usb-gadget.target new file mode 100644 index 000000000..c666683a9 --- /dev/null +++ b/units/usb-gadget.target @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: LGPL-2.1+ +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Hardware activated USB gadget +Documentation=man:systemd.special(7) diff --git a/units/user/default.target b/units/user/default.target index da319ce67..c50638c91 100644 --- a/units/user/default.target +++ b/units/user/default.target @@ -8,7 +8,7 @@ # (at your option) any later version. [Unit] -Description=Default +Description=Main User Target Documentation=man:systemd.special(7) Requires=basic.target After=basic.target diff --git a/units/user@.service.in b/units/user@.service.in index ca98c0515..e898b3978 100644 --- a/units/user@.service.in +++ b/units/user@.service.in @@ -24,3 +24,4 @@ KillMode=mixed Delegate=pids memory TasksMax=infinity TimeoutStopSec=120s +KeyringMode=inherit